"""Installed snapshots keep the first-build closure across later catalog stamps."""

from uuid import uuid4

import pytest

from src.domain.composition_recipes.installed_store import (
    overlay_snapshot_on_plan,
    snapshot_write_decision,
)
from src.domain.composition_recipes.snapshots import (
    InstalledCompositionSnapshot,
    InstalledFeaturePack,
)


def _snapshot() -> InstalledCompositionSnapshot:
    return InstalledCompositionSnapshot(
        project_id=uuid4(),
        recipe_id="shop",
        recipe_version=2,
        feature_packs=(
            InstalledFeaturePack(
                requested_id="contact-capture",
                canonical_id="contact-capture",
                version="1",
            ),
        ),
        app_blocks=("forms.contact",),
    )


def test_overlay_replaces_restamped_blocks_and_keeps_form_definitions():
    plan = {
        "composition": {
            "app_block_ids": ["reporting.dashboard"],
            "feature_pack_ids": ["analytics-pack"],
            "recipe_ids": ["other"],
            "recipe_versions": {"other": 1},
            "form_definitions": {"contact": {"id": "contact"}},
        }
    }
    merged = overlay_snapshot_on_plan(plan, _snapshot())
    composition = merged["composition"]
    assert composition["app_block_ids"] == ["forms.contact"]
    assert composition["feature_pack_ids"] == ["contact-capture"]
    assert composition["recipe_ids"] == ["shop"]
    assert composition["recipe_versions"] == {"shop": 2}
    assert composition["form_definitions"] == {"contact": {"id": "contact"}}


def test_snapshot_write_decision_is_initial_once_and_explicit_only_later():
    assert snapshot_write_decision(
        has_existing=False,
        explicit_block_ids=[],
        blocks_changed=False,
        initial=True,
    )
    assert not snapshot_write_decision(
        has_existing=True,
        explicit_block_ids=[],
        blocks_changed=False,
        initial=True,
    )
    assert not snapshot_write_decision(
        has_existing=True,
        explicit_block_ids=[],
        blocks_changed=True,
        initial=False,
    )
    assert snapshot_write_decision(
        has_existing=True,
        explicit_block_ids=["booking.appointment"],
        blocks_changed=True,
        initial=False,
    )
    assert not snapshot_write_decision(
        has_existing=True,
        explicit_block_ids=["booking.appointment"],
        blocks_changed=False,
        initial=False,
    )


@pytest.mark.asyncio
async def test_session_writes_the_first_snapshot_and_overlays_it():
    from datetime import UTC, datetime

    from src.domain.codegen.composition_reconcile import compile_reconciled_plan
    from src.domain.codegen.generation_plan import GenerationPlanV1
    from src.domain.composition_recipes.installed_store import (
        overlay_installed_baseline,
        write_initial_snapshot,
    )
    from tests.test_composition_reconcile import CONTACT_PROMPT, _discovery

    class _Result:
        def __init__(self, row):
            self._row = row

        def scalar_one_or_none(self):
            return self._row

    class MemorySession:
        def __init__(self):
            self.rows = []

        def add(self, row):
            if getattr(row, "created_at", None) is None:
                row.created_at = datetime.now(UTC)
            self.rows.append(row)

        async def flush(self):
            return None

        async def execute(self, _statement):
            return _Result(self.rows[-1] if self.rows else None)

    raw = compile_reconciled_plan(
        _discovery(),
        source_prompt=CONTACT_PROMPT,
        baseline=None,
        project_id=uuid4(),
        source_discovery_revision=1,
    )
    raw.pop("_explicit_blocks_admitted", None)
    raw.pop("_explicit_blocks_removed", None)
    plan = GenerationPlanV1.model_validate(raw)
    session = MemorySession()
    assert await write_initial_snapshot(session, plan.metadata.project_id, plan)
    assert not await write_initial_snapshot(session, plan.metadata.project_id, plan)
    assert len(session.rows) == 1
    restamp = {
        "composition": {
            "app_block_ids": ["reporting.dashboard"],
            "feature_pack_ids": ["analytics-pack"],
            "form_definitions": {"contact": {"id": "contact"}},
        }
    }
    overlaid = await overlay_installed_baseline(session, plan.metadata.project_id, restamp)
    assert "forms.contact" in overlaid["composition"]["app_block_ids"]
    assert "reporting.dashboard" not in overlaid["composition"]["app_block_ids"]
    assert overlaid["composition"]["form_definitions"] == {"contact": {"id": "contact"}}
