"""Tests for #1228 — file_ids persistence on summary records.

Verifies that file_ids computed during compaction are stored
in the summary record and visible through describe output.
"""

from __future__ import annotations

import json
import pytest
import pytest_asyncio

from cortex.hippo.config import HippoConfig
from cortex.hippo.engine import HippoEngine
from cortex.hippo.store.summary_store import SummaryStore


@pytest_asyncio.fixture
async def engine():
    config = HippoConfig(
        enabled=True,
        db_path=":memory:",
        fresh_tail_count=2,
        max_tokens=10_000,
    )
    eng = HippoEngine(config=config)
    await eng.initialize(db_path=":memory:", owner_id="test", conversation_id="test-sess")
    yield eng
    await eng.shutdown()


class TestFileIdsPersistence:
    """Verify file_ids are persisted on summary creation."""

    @pytest.mark.asyncio
    async def test_create_summary_with_file_ids(self, engine: HippoEngine):
        """create_summary() stores file_ids when provided."""
        summary_store = engine.summary_store
        conv = engine._conversation

        file_ids = json.dumps(["file_abc", "file_def"])
        record = await summary_store.create_summary(
            conversation_id=conv.conversation_id,
            owner_id="test",
            kind="leaf",
            depth=0,
            content="Summary mentioning file_abc and file_def",
            token_count=10,
            file_ids=file_ids,
        )
        assert record.file_ids == file_ids
        parsed = json.loads(record.file_ids)
        assert "file_abc" in parsed
        assert "file_def" in parsed

    @pytest.mark.asyncio
    async def test_create_summary_default_file_ids(self, engine: HippoEngine):
        """create_summary() defaults to '[]' when file_ids not provided."""
        summary_store = engine.summary_store
        conv = engine._conversation

        record = await summary_store.create_summary(
            conversation_id=conv.conversation_id,
            owner_id="test",
            kind="leaf",
            depth=0,
            content="No files here",
            token_count=5,
        )
        assert record.file_ids == "[]"

    @pytest.mark.asyncio
    async def test_file_ids_survive_fetch(self, engine: HippoEngine):
        """file_ids persist through DB round-trip."""
        summary_store = engine.summary_store
        conv = engine._conversation

        file_ids = json.dumps(["file_xyz"])
        created = await summary_store.create_summary(
            conversation_id=conv.conversation_id,
            owner_id="test",
            kind="leaf",
            depth=0,
            content="Has file reference",
            token_count=5,
            file_ids=file_ids,
        )

        # Fetch back — invalidate cache to force DB round-trip
        summary_store.cache.invalidate(created.summary_id)
        fetched = await summary_store.get_summary("test", created.summary_id)
        assert fetched is not None
        assert fetched.file_ids == file_ids
        assert json.loads(fetched.file_ids) == ["file_xyz"]

    @pytest.mark.asyncio
    async def test_file_ids_in_conversation_summaries(self, engine: HippoEngine):
        """file_ids appear in get_summaries_for_conversation results."""
        summary_store = engine.summary_store
        conv = engine._conversation

        file_ids = json.dumps(["file_001", "file_002"])
        await summary_store.create_summary(
            conversation_id=conv.conversation_id,
            owner_id="test",
            kind="leaf",
            depth=0,
            content="Summary with files",
            token_count=8,
            file_ids=file_ids,
        )

        summaries = await summary_store.get_summaries_for_conversation(
            conv.conversation_id, "test"
        )
        assert len(summaries) >= 1
        found = [s for s in summaries if json.loads(s.file_ids) == ["file_001", "file_002"]]
        assert len(found) == 1


class TestLeafPassFileIds:
    """Verify _leaf_pass persists file_ids extracted from messages."""

    @pytest.mark.asyncio
    async def test_leaf_compaction_stores_file_ids(self):
        """Full compaction cycle: messages with file refs → summary with file_ids."""
        config = HippoConfig(
            enabled=True,
            db_path=":memory:",
            fresh_tail_count=2,
            max_tokens=10_000,
            leaf_chunk_tokens=50,  # Low so compaction triggers easily
        )
        eng = HippoEngine(config=config)
        await eng.initialize(db_path=":memory:", owner_id="test", conversation_id="test-leaf")

        try:
            conv = eng._conversation

            # Ingest enough messages to trigger compaction, with file refs
            # _extract_file_ids requires pattern: file_[a-f0-9]{8,}
            for i in range(10):
                content = f"Message {i} references file_abcdef01 for context. Padding text here. " * 5
                await eng.ingest_message(
                    role="user" if i % 2 == 0 else "assistant",
                    content=content,
                )

            # Run compaction with a mock summarize function
            async def mock_summarize(text, aggressive=False, options=None):
                from cortex.hippo.summarize import SummarizeResult
                return SummarizeResult(
                    content=f"Summary. References: file_abcdef01, file_00112233aabb. {text[:50]}",
                    quality_score=0.8,
                    quality_breakdown={},
                    source="content",
                    mode_used="normal",
                )

            # Force compaction directly via compaction engine
            assert eng._compaction_engine is not None
            ce_result = await eng._compaction_engine.compact_leaf(
                conversation_id=conv.conversation_id,
                token_budget=10_000,
                summarize=mock_summarize,
                force=True,
            )

            # Check that summaries were created with file_ids
            summaries = await eng.summary_store.get_summaries_for_conversation(
                conv.conversation_id, "test"
            )
            assert len(summaries) >= 1

            # At least one summary should have file_ids populated
            has_file_ids = any(
                json.loads(s.file_ids) != [] for s in summaries
            )
            assert has_file_ids, f"No summary has file_ids; got: {[s.file_ids for s in summaries]}"
        finally:
            await eng.shutdown()
