"""Tests for metacognitive reflection engine."""
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from neo_cortex.reflection import (
    generate_focal_points,
    generate_insight,
    verify_truthfulness,
)


@pytest.fixture
def mock_llm():
    """Mock LLM client."""
    client = AsyncMock()
    return client


@pytest.fixture
def sample_memories():
    """Recent memories for reflection."""
    return [
        {"id": "m1", "summary": "Fixed bug in digestor using Protocol D", "concepts": ["debugging", "protocol-d"]},
        {"id": "m2", "summary": "Refactored cortex recall to use RRF fusion", "concepts": ["refactoring", "recall"]},
        {"id": "m3", "summary": "Wrote 12 tests before implementing decay", "concepts": ["testing", "tdd", "decay"]},
        {"id": "m4", "summary": "Debugged hanging test by reading error first", "concepts": ["debugging", "testing"]},
        {"id": "m5", "summary": "Decided to use execution days not calendar days", "concepts": ["architecture", "decay"]},
    ]


class TestGenerateFocalPoints:
    """Focal point generation from recent memories."""

    @pytest.mark.asyncio
    async def test_returns_three_focal_points(self, mock_llm, sample_memories):
        mock_llm.call.return_value = (
            "1. How do I approach debugging when tests fail?\n"
            "2. What pattern emerges in my architectural decisions?\n"
            "3. How does TDD influence my implementation strategy?"
        )
        result = await generate_focal_points(mock_llm, sample_memories)
        assert len(result) == 3
        assert all(isinstance(fp, str) for fp in result)

    @pytest.mark.asyncio
    async def test_focal_points_are_questions(self, mock_llm, sample_memories):
        mock_llm.call.return_value = (
            "1. How do I approach debugging?\n"
            "2. What drives my architecture choices?\n"
            "3. When do I write tests first?"
        )
        result = await generate_focal_points(mock_llm, sample_memories)
        assert all("?" in fp for fp in result)

    @pytest.mark.asyncio
    async def test_handles_malformed_llm_output(self, mock_llm, sample_memories):
        mock_llm.call.return_value = "Just one line without structure"
        result = await generate_focal_points(mock_llm, sample_memories)
        assert len(result) >= 1  # At least returns something


class TestGenerateInsight:
    """Metacognitive insight generation with evidence."""

    @pytest.mark.asyncio
    async def test_returns_insight_with_evidence_ids(self, mock_llm):
        mock_llm.call.return_value = (
            'INSIGHT: I consistently use Protocol D for debugging — read error first, isolate, then fix.\n'
            'EVIDENCE: m1, m4'
        )
        focal_point = "How do I approach debugging?"
        evidence = [
            {"id": "m1", "summary": "Fixed bug using Protocol D"},
            {"id": "m4", "summary": "Debugged by reading error first"},
        ]
        result = await generate_insight(mock_llm, focal_point, evidence)
        assert result["text"] is not None
        assert len(result["text"]) > 10
        assert "evidence_ids" in result
        assert len(result["evidence_ids"]) > 0

    @pytest.mark.asyncio
    async def test_evidence_ids_are_valid_memory_ids(self, mock_llm):
        mock_llm.call.return_value = (
            'INSIGHT: I prefer TDD approach.\n'
            'EVIDENCE: m3, m5'
        )
        evidence = [
            {"id": "m3", "summary": "Wrote tests first"},
            {"id": "m5", "summary": "Decided architecture with tests"},
        ]
        result = await generate_insight(mock_llm, "When do I write tests?", evidence)
        for eid in result["evidence_ids"]:
            assert eid in [e["id"] for e in evidence]


class TestVerifyTruthfulness:
    """Truthfulness verification of generated insights."""

    @pytest.mark.asyncio
    async def test_supported_insight_passes(self, mock_llm):
        mock_llm.call.return_value = "SUPPORTED: The evidence clearly shows systematic debugging."
        result = await verify_truthfulness(
            mock_llm,
            claim="I debug systematically using Protocol D",
            evidence=[{"id": "m1", "summary": "Fixed bug using Protocol D"}],
        )
        assert result is True

    @pytest.mark.asyncio
    async def test_unsupported_insight_fails(self, mock_llm):
        mock_llm.call.return_value = "UNSUPPORTED: Evidence does not mention this behavior."
        result = await verify_truthfulness(
            mock_llm,
            claim="I always write documentation first",
            evidence=[{"id": "m1", "summary": "Fixed bug using Protocol D"}],
        )
        assert result is False

    @pytest.mark.asyncio
    async def test_ambiguous_response_defaults_to_false(self, mock_llm):
        mock_llm.call.return_value = "Maybe, it's unclear from the evidence."
        result = await verify_truthfulness(
            mock_llm,
            claim="I prefer functional programming",
            evidence=[{"id": "m1", "summary": "Used classes everywhere"}],
        )
        assert result is False
