"""Tests for the extracted _core_ingest() method and consolidated ingest paths."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from neo_cortex.models import (
    Activity,
    MemoryRecord,
    ObservationLevel,
    StructuredFields,
)


@pytest.fixture
def cortex_service():
    """Create a CortexService with mocked dependencies."""
    from neo_cortex.cortex import CortexService

    store = MagicMock()
    store.insert = MagicMock()
    store.count = MagicMock(return_value=0)
    embedder = AsyncMock()
    embedder.embed_passage = AsyncMock(return_value=[0.1] * 1024)
    classifier = AsyncMock()

    service = CortexService(store=store, embedder=embedder, classifier=classifier)
    service._index = MagicMock()
    service._index.insert = MagicMock()
    service._index.update = MagicMock()
    service._index.get_structured = MagicMock(return_value=None)
    service._index.add_pending_merge = MagicMock()
    service._dedup = MagicMock()
    service._dedup.is_duplicate = MagicMock(return_value=False)
    service._dedup.add = MagicMock()
    service._graph = MagicMock()
    service._graph.add_memory = MagicMock()
    service._classifier = AsyncMock()
    service._llm = AsyncMock()
    return service


class TestCoreIngest:
    @pytest.mark.asyncio
    async def test_core_ingest_stores_in_chromadb(self, cortex_service):
        record = MemoryRecord(id="test-1", session_id="s1", document="test content")
        result = await cortex_service._core_ingest(
            content="test content",
            embedding=[0.1] * 1024,
            record=record,
            structured=None,
        )
        assert result == "test-1"
        cortex_service._store.insert.assert_called_once()

    @pytest.mark.asyncio
    async def test_core_ingest_stores_in_index(self, cortex_service):
        record = MemoryRecord(id="test-2", session_id="s1", document="test content")
        await cortex_service._core_ingest(
            content="test content",
            embedding=[0.1] * 1024,
            record=record,
            structured=None,
        )
        cortex_service._index.insert.assert_called_once()

    @pytest.mark.asyncio
    async def test_core_ingest_adds_to_graph(self, cortex_service):
        record = MemoryRecord(id="test-3", session_id="s1", document="test content")
        structured = StructuredFields(concepts=["python", "testing"])
        await cortex_service._core_ingest(
            content="test content",
            embedding=[0.1] * 1024,
            record=record,
            structured=structured,
        )
        cortex_service._graph.add_memory.assert_called_once_with("test-3", ["python", "testing"])

    @pytest.mark.asyncio
    async def test_core_ingest_dedup_filters(self, cortex_service):
        cortex_service._dedup.is_duplicate.return_value = True
        record = MemoryRecord(id="test-4", session_id="s1", document="duplicate")
        result = await cortex_service._core_ingest(
            content="duplicate",
            embedding=[0.1] * 1024,
            record=record,
            structured=None,
        )
        assert result is None
        cortex_service._store.insert.assert_not_called()

    @pytest.mark.asyncio
    async def test_core_ingest_with_conflict_resolution(self, cortex_service):
        record = MemoryRecord(id="test-5", session_id="s1", document="new info")
        cortex_service._resolve_conflicts = AsyncMock(return_value={"action": "add"})
        result = await cortex_service._core_ingest(
            content="new info",
            embedding=[0.1] * 1024,
            record=record,
            structured=None,
            resolve_conflicts=True,
        )
        assert result == "test-5"
        cortex_service._resolve_conflicts.assert_called_once()

    @pytest.mark.asyncio
    async def test_core_ingest_without_conflict_resolution(self, cortex_service):
        record = MemoryRecord(id="test-6", session_id="s1", document="fast ingest")
        cortex_service._resolve_conflicts = AsyncMock()
        result = await cortex_service._core_ingest(
            content="fast ingest",
            embedding=[0.1] * 1024,
            record=record,
            structured=None,
            resolve_conflicts=False,
        )
        assert result == "test-6"
        cortex_service._resolve_conflicts.assert_not_called()

    @pytest.mark.asyncio
    async def test_core_ingest_records_pending_merges(self, cortex_service):
        record = MemoryRecord(id="test-7", session_id="s1", document="fast ingest")
        similar_rec = MagicMock()
        similar_rec.id = "existing-1"
        cortex_service._store.count = MagicMock(return_value=5)
        cortex_service._store.query = MagicMock(
            return_value=[(similar_rec, 0.85)]
        )
        result = await cortex_service._core_ingest(
            content="fast ingest",
            embedding=[0.1] * 1024,
            record=record,
            structured=None,
            resolve_conflicts=False,
            record_merges=True,
        )
        assert result == "test-7"


class TestIngestPathsUseCore:
    @pytest.mark.asyncio
    async def test_ingest_delegates_to_core(self, cortex_service):
        """ingest() must call _core_ingest after noise filter + classify."""
        cortex_service._core_ingest = AsyncMock(return_value="m-1")
        cortex_service._classifier.classify = AsyncMock(
            return_value=MagicMock(
                project="test", topic="unit-test", activity=Activity.FEATURE,
                concepts=[], files_touched=[], observation_level="direct",
                title="Test", summary="Test summary", facts=[],
            )
        )
        cortex_service._classifier.is_noise = AsyncMock(return_value=False)
        from neo_cortex.models import IngestRequest
        # Long enough question to skip is_noise OR mock returns False
        req = IngestRequest(session_id="s1", question="test?", answer="yes")
        result = await cortex_service.ingest(req)
        cortex_service._core_ingest.assert_called_once()

    @pytest.mark.asyncio
    async def test_ingest_episode_delegates_to_core(self, cortex_service):
        """ingest_episode() must call _core_ingest after classify."""
        cortex_service._core_ingest = AsyncMock(return_value="m-2")
        cortex_service._classifier.classify = AsyncMock(
            return_value=MagicMock(
                project="test", topic="episode", activity=Activity.DISCUSSION,
                concepts=[], files_touched=[], observation_level="direct",
                title="Episode", summary="Episode summary", facts=[],
            )
        )
        result = await cortex_service.ingest_episode(
            text="episode content", session_id="s1"
        )
        cortex_service._core_ingest.assert_called_once()

    @pytest.mark.asyncio
    async def test_ingest_knowledge_delegates_to_core(self, cortex_service):
        """ingest_knowledge() must call _core_ingest with resolve_conflicts=True."""
        cortex_service._core_ingest = AsyncMock(return_value="m-3")
        result = await cortex_service.ingest_knowledge(
            content="knowledge content",
            topic="testing",
            project="neo-cortex",
            entry_type="lesson",
            session_id="s1",
        )
        cortex_service._core_ingest.assert_called_once()
        call_kwargs = cortex_service._core_ingest.call_args.kwargs
        assert call_kwargs.get("resolve_conflicts") is True

    @pytest.mark.asyncio
    async def test_ingest_knowledge_fast_delegates_to_core(self, cortex_service):
        """ingest_knowledge_fast() must call _core_ingest with resolve_conflicts=False."""
        cortex_service._core_ingest = AsyncMock(return_value="m-4")
        result = await cortex_service.ingest_knowledge_fast(
            content="fast content",
            topic="testing",
            project="neo-cortex",
            entry_type="lesson",
            session_id="s1",
        )
        cortex_service._core_ingest.assert_called_once()
        call_kwargs = cortex_service._core_ingest.call_args.kwargs
        assert call_kwargs.get("resolve_conflicts") is False
