"""Tests for GroqClassifier — mocked HTTP."""

import json

import pytest
import httpx
from pytest_httpx import HTTPXMock

from neo_cortex.classifier import GroqClassifier, _strip_code_fences
from neo_cortex.models import Activity


def _groq_response(content: str) -> dict:
    """Build a mock Groq API response."""
    return {"choices": [{"message": {"content": content}}]}


@pytest.fixture
def classifier():
    return GroqClassifier(api_key="test-key")


class TestStripCodeFences:
    def test_strips_json_fences(self):
        assert _strip_code_fences('```json\n{"a": 1}\n```') == '{"a": 1}'

    def test_strips_bare_fences(self):
        assert _strip_code_fences('```\n{"a": 1}\n```') == '{"a": 1}'

    def test_no_fences_unchanged(self):
        assert _strip_code_fences('{"a": 1}') == '{"a": 1}'


class TestGroqClassify:
    @pytest.mark.asyncio
    async def test_classify_returns_result(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response(
            '{"project": "neo-cortex", "topic": "memory store", "activity": "feature"}'
        ))
        result = await classifier.classify("How do I add a store?", "Use SQLite.")
        assert result.project == "neo-cortex"
        assert result.topic == "memory store"
        assert result.activity == Activity.FEATURE

    @pytest.mark.asyncio
    async def test_classify_with_code_fences(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response(
            '```json\n{"project": "general", "topic": "test", "activity": "debug"}\n```'
        ))
        result = await classifier.classify("What?", "Debug it.")
        assert result.project == "general"
        assert result.activity == Activity.DEBUG

    @pytest.mark.asyncio
    async def test_classify_unknown_activity_falls_back(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response(
            '{"project": "x", "topic": "y", "activity": "unknown_thing"}'
        ))
        result = await classifier.classify("Q", "A")
        assert result.activity == Activity.DISCUSSION

    @pytest.mark.asyncio
    async def test_classify_api_error_returns_default(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(status_code=500)
        result = await classifier.classify("Q", "A")
        assert result.project == "general"
        assert result.activity == Activity.DISCUSSION


class TestGroqNoise:
    @pytest.mark.asyncio
    async def test_noise_true(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response('{"noise": true, "why": "greeting"}'))
        assert await classifier.is_noise("ciao") is True

    @pytest.mark.asyncio
    async def test_noise_false(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response('{"noise": false, "why": "real question"}'))
        assert await classifier.is_noise("How do I fix this bug?") is False

    @pytest.mark.asyncio
    async def test_noise_api_error_defaults_false(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(status_code=500)
        assert await classifier.is_noise("test") is False


class TestGroqAnalyzeQuery:
    @pytest.mark.asyncio
    async def test_analyze_returns_analysis(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response(json.dumps({
            "project": "neo-reloaded",
            "topic": "engine",
            "activity": "bugfix",
            "time_hint": "recent",
            "refined_query": "neo-reloaded engine bugfix",
        })))
        result = await classifier.analyze_query("recent bugs in engine")
        assert result.project == "neo-reloaded"
        assert result.time_hint == "recent"
        assert result.refined_query == "neo-reloaded engine bugfix"

    @pytest.mark.asyncio
    async def test_analyze_api_error_returns_passthrough(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(status_code=500)
        result = await classifier.analyze_query("my query")
        assert result.refined_query == "my query"
        assert result.project is None


class TestClassifyStructured:
    @pytest.mark.asyncio
    async def test_classify_returns_title_and_summary(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response(json.dumps({
            "project": "neo-cortex",
            "topic": "sqlite",
            "activity": "feature",
            "title": "Add SQLite memory index",
            "summary": "Created MemoryIndex with FTS5 for keyword search",
            "facts": ["SQLite FTS5 for search", "INSERT OR REPLACE for upsert"],
            "concepts": ["full-text-search", "sqlite"],
            "files_touched": ["src/neo_cortex/memory_index.py"],
        })))
        result = await classifier.classify("Add SQLite index", "Created MemoryIndex...")
        assert result.title == "Add SQLite memory index"
        assert result.summary is not None
        assert "SQLite FTS5 for search" in result.facts
        assert "full-text-search" in result.concepts
        assert "src/neo_cortex/memory_index.py" in result.files_touched

    @pytest.mark.asyncio
    async def test_classify_missing_structured_fields_graceful(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response(
            '{"project": "general", "topic": "test", "activity": "discussion"}'
        ))
        result = await classifier.classify("Q", "A")
        assert result.project == "general"
        assert result.title is None
        assert result.facts == []
        assert result.concepts == []

    @pytest.mark.asyncio
    async def test_classify_partial_structured_fields(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(json=_groq_response(json.dumps({
            "project": "neo-reloaded",
            "topic": "bugfix",
            "activity": "bugfix",
            "title": "Fix crash",
            "facts": ["HasExited throws after dispose"],
        })))
        result = await classifier.classify("Fix crash?", "Done.")
        assert result.title == "Fix crash"
        assert len(result.facts) == 1
        assert result.concepts == []
        assert result.files_touched == []

    @pytest.mark.asyncio
    async def test_classify_api_error_returns_empty_structured(self, classifier, httpx_mock: HTTPXMock):
        httpx_mock.add_response(status_code=500)
        result = await classifier.classify("Q", "A")
        assert result.title is None
        assert result.facts == []


class TestGroqAggregate:
    @pytest.mark.asyncio
    async def test_aggregate_empty_returns_default(self, classifier):
        result = await classifier.aggregate_to_mbel([])
        assert "empty" in result

    @pytest.mark.asyncio
    async def test_aggregate_returns_mbel(self, classifier, httpx_mock: HTTPXMock):
        from neo_cortex.models import MemoryRecord, RecalledMemory
        mem = RecalledMemory(
            record=MemoryRecord(
                id="test1", session_id="s1", question="Q1",
                answer_preview="A1", document="Q: Q1\nA: A1",
            ),
            similarity=0.9,
        )
        httpx_mock.add_response(json=_groq_response("@recall::Q1{bugfix}"))
        result = await classifier.aggregate_to_mbel([mem])
        assert "@recall" in result
