"""Integration tests — config change (monthly → topical rollups)."""

from __future__ import annotations

import shutil
from pathlib import Path

import pytest

from synix import Layer, Pipeline, Projection
from synix.pipeline.runner import run
from synix.search.index import SearchIndex

FIXTURES_DIR = Path(__file__).parent.parent / "synix" / "fixtures"


@pytest.fixture
def source_dir(tmp_path):
    """Source directory with both export fixtures."""
    src = tmp_path / "exports"
    src.mkdir()
    shutil.copy(FIXTURES_DIR / "chatgpt_export.json", src / "chatgpt_export.json")
    shutil.copy(FIXTURES_DIR / "claude_export.json", src / "claude_export.json")
    return src


@pytest.fixture
def build_dir(tmp_path):
    return tmp_path / "build"


def _monthly_pipeline(build_dir: Path) -> Pipeline:
    """Pipeline with monthly rollups."""
    p = Pipeline("monthly-pipeline")
    p.build_dir = str(build_dir)
    p.llm_config = {"model": "claude-sonnet-4-20250514", "temperature": 0.3, "max_tokens": 1024}

    p.add_layer(Layer(name="transcripts", level=0, transform="parse"))
    p.add_layer(
        Layer(
            name="episodes",
            level=1,
            depends_on=["transcripts"],
            transform="episode_summary",
            grouping="by_conversation",
        )
    )
    p.add_layer(
        Layer(name="monthly", level=2, depends_on=["episodes"], transform="monthly_rollup", grouping="by_month")
    )
    p.add_layer(
        Layer(
            name="core",
            level=3,
            depends_on=["monthly"],
            transform="core_synthesis",
            grouping="single",
            context_budget=10000,
        )
    )

    p.add_projection(
        Projection(
            name="memory-index",
            projection_type="search_index",
            sources=[
                {"layer": "episodes", "search": ["fulltext"]},
                {"layer": "monthly", "search": ["fulltext"]},
                {"layer": "core", "search": ["fulltext"]},
            ],
        )
    )
    p.add_projection(
        Projection(
            name="context-doc",
            projection_type="flat_file",
            sources=[{"layer": "core"}],
            config={"output_path": str(build_dir / "context.md")},
        )
    )
    return p


def _topical_pipeline(build_dir: Path) -> Pipeline:
    """Pipeline with topical rollups instead of monthly."""
    p = Pipeline("topical-pipeline")
    p.build_dir = str(build_dir)
    p.llm_config = {"model": "claude-sonnet-4-20250514", "temperature": 0.3, "max_tokens": 1024}

    p.add_layer(Layer(name="transcripts", level=0, transform="parse"))
    p.add_layer(
        Layer(
            name="episodes",
            level=1,
            depends_on=["transcripts"],
            transform="episode_summary",
            grouping="by_conversation",
        )
    )
    p.add_layer(
        Layer(
            name="topics",
            level=2,
            depends_on=["episodes"],
            transform="topical_rollup",
            grouping="by_topic",
            config={"topics": ["programming", "machine-learning"]},
        )
    )
    p.add_layer(
        Layer(
            name="core",
            level=3,
            depends_on=["topics"],
            transform="core_synthesis",
            grouping="single",
            context_budget=10000,
        )
    )

    p.add_projection(
        Projection(
            name="memory-index",
            projection_type="search_index",
            sources=[
                {"layer": "episodes", "search": ["fulltext"]},
                {"layer": "topics", "search": ["fulltext"]},
                {"layer": "core", "search": ["fulltext"]},
            ],
        )
    )
    p.add_projection(
        Projection(
            name="context-doc",
            projection_type="flat_file",
            sources=[{"layer": "core"}],
            config={"output_path": str(build_dir / "context.md")},
        )
    )
    return p


class TestConfigChange:
    def test_swap_monthly_to_topical(self, source_dir, build_dir, mock_llm):
        """Run monthly then topical — transcripts+episodes cached, topics+core rebuilt."""
        monthly = _monthly_pipeline(build_dir)
        result1 = run(monthly, source_dir=str(source_dir))
        assert result1.built > 0

        topical = _topical_pipeline(build_dir)
        result2 = run(topical, source_dir=str(source_dir))

        # Transcripts: all cached
        t_stats = next(s for s in result2.layer_stats if s.name == "transcripts")
        assert t_stats.built == 0
        assert t_stats.cached > 0

        # Episodes: all cached
        e_stats = next(s for s in result2.layer_stats if s.name == "episodes")
        assert e_stats.built == 0
        assert e_stats.cached > 0

        # Topics: all built (new layer)
        topic_stats = next(s for s in result2.layer_stats if s.name == "topics")
        assert topic_stats.built > 0

        # Core: rebuilt (dependency changed)
        core_stats = next(s for s in result2.layer_stats if s.name == "core")
        assert core_stats.built > 0

    def test_search_results_differ(self, source_dir, build_dir, mock_llm):
        """Same query returns different results after config change."""
        monthly = _monthly_pipeline(build_dir)
        run(monthly, source_dir=str(source_dir))

        index1 = SearchIndex(build_dir / "search.db")
        results1 = index1.query("programming")
        ids1 = {r.label for r in results1}
        index1.close()

        topical = _topical_pipeline(build_dir)
        run(topical, source_dir=str(source_dir))

        index2 = SearchIndex(build_dir / "search.db")
        results2 = index2.query("programming")
        ids2 = {r.label for r in results2}
        index2.close()

        # The rollup artifact IDs should differ (monthly-* vs topic-*)
        rollup_ids1 = {i for i in ids1 if i.startswith("monthly-")}
        rollup_ids2 = {i for i in ids2 if i.startswith("topic-")}
        assert rollup_ids1 != rollup_ids2

    def test_context_doc_differs(self, source_dir, build_dir, mock_llm):
        """context.md content changes after config change."""
        monthly = _monthly_pipeline(build_dir)
        run(monthly, source_dir=str(source_dir))
        content1 = (build_dir / "context.md").read_text()

        topical = _topical_pipeline(build_dir)
        run(topical, source_dir=str(source_dir))
        content2 = (build_dir / "context.md").read_text()

        # The core memory is rebuilt with different inputs, so content
        # is the same mock text. What matters is both exist and the file is rewritten.
        assert len(content1) > 0
        assert len(content2) > 0
