"""REC-231 Phase 5 — scene-VERSION read-model acceptance tests.

``readmodel.get_scene_versions`` is the pointer-faithful scene-version projection (the
sibling of ``get_board``). A flat batch projects as a single v1-active entry; a versioned
batch projects ``active_version`` + every registered version + the newer-unpointed count.
Pure projection — it never writes and never moves the pointer.
"""

from __future__ import annotations

from pathlib import Path

import pytest

from recoil.api.schemas.scene_version import SceneVersionsModel
from recoil.pipeline.core.persistence import (
    load_manifest,
    save_scene,
    scene_path,
)
from recoil.pipeline.core.scene_version_store import SceneVersionStore
from recoil.pipeline.core.take import Beat, Scene
from recoil.workspace import readmodel as rm

EP = "ep_001"
BATCH = "BATCH_001"
SELECTOR = "EP001_CONT_001"


def _make_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> str:
    projects_root = tmp_path / "projects"
    projects_root.mkdir()
    (projects_root / ".recoil-data-root").write_text("recoil-data-root\n")
    project = "fixture"
    project_root = projects_root / project
    project_root.mkdir()
    (project_root / "project_config.json").write_text("{}")
    scenes_dir = project_root / "_pipeline" / "state" / "orchestration" / "scenes"
    scenes_dir.mkdir(parents=True)
    monkeypatch.setenv("RECOIL_PROJECTS_ROOT", str(projects_root))
    return project


def _scene(scene_id: str, beat_ids: list[str]) -> Scene:
    return Scene(scene_id=scene_id, beats=[Beat(beat_id=b) for b in beat_ids])


def test_scene_versions_flat_is_v1_active(tmp_path, monkeypatch):
    """No manifest → exactly one v1-active/approved/derived entry whose shot_count is the
    flat body's beat count; newer_unpointed_versions == 0 (the no-flag-day default)."""
    project = _make_project(tmp_path, monkeypatch)
    # flat body with 2 beats, NO manifest
    save_scene(_scene(BATCH, ["OPENING", "REACTION"]), scene_path(project, EP, BATCH))
    assert load_manifest(project, EP, BATCH) is None  # genuinely un-versioned

    m = rm.get_scene_versions(SELECTOR, project)

    assert isinstance(m, SceneVersionsModel)
    assert m.active_version == 1
    assert m.newer_unpointed_versions == 0
    assert len(m.versions) == 1
    v1 = m.versions[0]
    assert v1.version == 1
    assert v1.state == "approved"
    assert v1.downstream == "derived"
    assert v1.kind == "reshoot"
    assert v1.artifact == "ep_001_BATCH_001.json"  # PINNED flat-v1 basename
    assert v1.shot_count == 2  # from the live flat body
    assert v1.structure_fingerprint is None
    # pure projection: no manifest materialized on read
    assert load_manifest(project, EP, BATCH) is None


def test_scene_versions_unpointed_diagnostic(tmp_path, monkeypatch):
    """v1 active + v2/v3 candidates → active_version stays 1, versions == [1,2,3], each
    carries state/downstream/kind/shot_count, newer_unpointed_versions == 2; the newest
    (v3) is NOT reported active."""
    project = _make_project(tmp_path, monkeypatch)
    # legacy flat v1 (1 beat) → first re-derive materializes the manifest with v2,
    # a second re-derive appends v3; the pointer never moves on append.
    save_scene(_scene(BATCH, ["OPENING"]), scene_path(project, EP, BATCH))
    store = SceneVersionStore(project, EP)
    assert store.write_scene_candidate(BATCH, _scene(BATCH, ["A", "B"])) == 2
    assert store.write_scene_candidate(BATCH, _scene(BATCH, ["A", "B", "C"])) == 3

    m = rm.get_scene_versions(SELECTOR, project)

    assert m.active_version == 1  # newest (v3) is NOT treated as active
    assert [v.version for v in m.versions] == [1, 2, 3]
    assert m.newer_unpointed_versions == 2  # v2 + v3 exceed the pointer
    by_v = {v.version: v for v in m.versions}
    assert by_v[1].state == "approved" and by_v[1].downstream == "derived"
    assert by_v[2].state == "candidate" and by_v[2].downstream == "not_derived"
    assert by_v[3].state == "candidate" and by_v[3].downstream == "not_derived"
    assert by_v[1].shot_count == 1 and by_v[2].shot_count == 2 and by_v[3].shot_count == 3
    assert all(v.kind == "reshoot" for v in m.versions)
    # structure_fingerprint is populated on real writes (Phase 3)
    assert all(v.structure_fingerprint for v in m.versions)


def test_get_scene_versions_is_a_pure_projection(tmp_path, monkeypatch):
    """A read never moves the pointer or mutates the manifest on disk."""
    project = _make_project(tmp_path, monkeypatch)
    save_scene(_scene(BATCH, ["OPENING"]), scene_path(project, EP, BATCH))
    store = SceneVersionStore(project, EP)
    store.write_scene_candidate(BATCH, _scene(BATCH, ["A", "B"]))
    before = load_manifest(project, EP, BATCH)

    rm.get_scene_versions(SELECTOR, project)
    rm.get_scene_versions(SELECTOR, project)

    assert load_manifest(project, EP, BATCH) == before  # byte-stable across reads
