"""Tests for identity review (graduation) tool."""
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from neo_cortex.reflection import identity_review


class TestIdentityReview:
    """Graduation: cluster mature insights and propose CLAUDE.md amendments."""

    @pytest.mark.asyncio
    async def test_returns_proposals_for_mature_clusters(self):
        cortex = AsyncMock()
        cortex._index = MagicMock()
        llm = AsyncMock()

        # Mature metacognitive memories (high energy + stability)
        cortex._index.get_metacognitive_memories.return_value = [
            {"id": "v3_meta_1", "summary": "I debug with Protocol D", "energy": 0.9, "stability": 0.7, "evidence_ids": "m1,m2,m3", "concepts": "debugging,protocol-d"},
            {"id": "v3_meta_2", "summary": "I read errors before acting", "energy": 0.85, "stability": 0.65, "evidence_ids": "m1,m4", "concepts": "debugging,errors"},
            {"id": "v3_meta_3", "summary": "I isolate problems systematically", "energy": 0.8, "stability": 0.6, "evidence_ids": "m1,m2,m5", "concepts": "debugging,isolation"},
        ]

        # LLM generates coherent proposal
        llm.call.side_effect = [
            # generate_identity_proposal
            "SECTION: §DEBUG\nPROPOSAL: @learned::systematicDebugging→protocolD+readError+isolate{evidence::#3insights}\nREASON: Three consistent insights over multiple sessions confirm systematic debugging pattern.",
            # check_coherence
            "COHERENT: Reinforces existing §DEBUG section without contradictions.",
        ]

        proposals = await identity_review(cortex, llm)
        assert len(proposals) >= 1
        assert "SECTION" in str(proposals[0]) or "section" in str(proposals[0])

    @pytest.mark.asyncio
    async def test_returns_empty_when_no_mature_insights(self):
        cortex = AsyncMock()
        cortex._index = MagicMock()
        llm = AsyncMock()
        cortex._index.get_metacognitive_memories.return_value = []

        proposals = await identity_review(cortex, llm)
        assert proposals == []
        llm.call.assert_not_called()

    @pytest.mark.asyncio
    async def test_filters_contradictory_proposals(self):
        cortex = AsyncMock()
        cortex._index = MagicMock()
        llm = AsyncMock()

        cortex._index.get_metacognitive_memories.return_value = [
            {"id": "v3_meta_1", "summary": "I rush through debugging", "energy": 0.8, "stability": 0.6, "evidence_ids": "m1", "concepts": "debugging"},
            {"id": "v3_meta_2", "summary": "I rush even more", "energy": 0.75, "stability": 0.55, "evidence_ids": "m2", "concepts": "debugging"},
            {"id": "v3_meta_3", "summary": "I always rush", "energy": 0.7, "stability": 0.5, "evidence_ids": "m3", "concepts": "debugging"},
        ]

        llm.call.side_effect = [
            "SECTION: §DEBUG\nPROPOSAL: rush through\nREASON: pattern",
            "CONTRADICTS: This contradicts the existing §DEBUG Protocol D methodology.",
        ]

        proposals = await identity_review(cortex, llm)
        assert len(proposals) == 0  # Contradictory proposal filtered

    @pytest.mark.asyncio
    async def test_proposals_include_evidence_trail(self):
        cortex = AsyncMock()
        cortex._index = MagicMock()
        llm = AsyncMock()

        cortex._index.get_metacognitive_memories.return_value = [
            {"id": "v3_meta_1", "summary": "I prefer TDD", "energy": 0.9, "stability": 0.8, "evidence_ids": "m1,m2,m3", "concepts": "testing,tdd"},
            {"id": "v3_meta_2", "summary": "Tests before code", "energy": 0.85, "stability": 0.7, "evidence_ids": "m2,m4", "concepts": "testing,tdd"},
            {"id": "v3_meta_3", "summary": "Red-green-refactor works", "energy": 0.8, "stability": 0.65, "evidence_ids": "m3,m5", "concepts": "testing,tdd"},
        ]

        llm.call.side_effect = [
            "SECTION: §COGITO\nPROPOSAL: @learned::TDD\nREASON: consistent",
            "COHERENT: Aligns with existing coding philosophy.",
        ]

        proposals = await identity_review(cortex, llm)
        assert len(proposals) == 1
        assert "evidence_count" in proposals[0]
        assert proposals[0]["evidence_count"] == 3


class TestIdentityReviewMCPTool:
    """MCP tool memory_identity_review wraps identity_review."""

    @pytest.mark.asyncio
    async def test_tool_exists(self):
        from neo_cortex import mcp as mcp_module
        assert hasattr(mcp_module, "memory_identity_review")

    @pytest.mark.asyncio
    async def test_tool_callable(self):
        from neo_cortex import mcp as mcp_module
        # .fn is the underlying function (FastMCP wraps it)
        assert callable(mcp_module.memory_identity_review.fn)
