"""Extended unit tests for plan_loader / CanonicalShot.

Covers JT's Change 1 requirements:
  * Flat-shots plan and sequences-shape plan converge to equivalent
    CanonicalPlan (same shot count, same field values).
  * Legacy `model` field maps to `previs_model`.
  * `video_model` defaults to None when absent; passes through when present.
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

import pytest

_REPO_ROOT = Path(__file__).resolve().parents[4]
if str(_REPO_ROOT) not in sys.path:
    sys.path.insert(0, str(_REPO_ROOT))

from recoil.pipeline._lib.plan_loader import load_plan  # noqa: E402


def _shot_dict(
    shot_id, scene, loc="L1", model="gemini-3-pro-image-preview", video_model=None
):
    d = {
        "shot_id": shot_id,
        "scene_index": scene,
        "pipeline": "still",
        "model": model,
        "routing_data": {"target_editorial_duration_s": 3, "is_env_only": False},
        "asset_data": {"location_id": loc, "characters": ["jade"]},
        "prompt_data": {"shot_type": "MS"},
    }
    if video_model is not None:
        d["video_model"] = video_model
    return d


def test_flat_and_sequences_converge_to_equivalent_shape(tmp_path):
    """Same logical content in two file shapes → same canonical shape."""
    flat = {
        "episode_id": "ep_001",
        "project": "fixture",
        "shots": [
            _shot_dict("EP001_SH01", 1),
            _shot_dict("EP001_SH02", 2),
        ],
    }
    seq = {
        "episode_id": "ep_001",
        "project": "fixture",
        "sequences": {
            "SEQ_001": [_shot_dict("EP001_SH01", 1), _shot_dict("EP001_SH02", 2)],
        },
    }
    pf = tmp_path / "flat.json"
    pf.write_text(json.dumps(flat))
    ps = tmp_path / "seq.json"
    ps.write_text(json.dumps(seq))
    plan_f = load_plan(pf)
    plan_s = load_plan(ps)

    assert len(plan_f.shots) == len(plan_s.shots) == 2
    # Per-shot fields match (sequence_id differs by design)
    for sf, ss in zip(plan_f.shots, plan_s.shots):
        assert sf.shot_id == ss.shot_id
        assert sf.scene_index == ss.scene_index
        assert sf.previs_model == ss.previs_model
        assert sf.video_model == ss.video_model
        assert sf.location_id == ss.location_id
        assert sf.characters == ss.characters
        assert sf.shot_type == ss.shot_type
        assert sf.duration_s == ss.duration_s
    # sequence_id differs
    assert plan_f.shots[0].sequence_id is None
    assert plan_s.shots[0].sequence_id == "SEQ_001"


def test_legacy_model_field_maps_to_previs_model(tmp_path):
    plan = {
        "episode_id": "ep_001",
        "project": "fixture",
        "shots": [_shot_dict("EP001_SH01", 1, model="gemini-3-pro-image-preview")],
    }
    p = tmp_path / "plan.json"
    p.write_text(json.dumps(plan))
    loaded = load_plan(p)
    assert loaded.shots[0].previs_model == "gemini-3-pro-image-preview"
    assert loaded.shots[0].video_model is None


def test_video_model_passes_through(tmp_path):
    plan = {
        "episode_id": "ep_001",
        "project": "fixture",
        "shots": [_shot_dict("EP001_SH01", 1, video_model="seeddance-2.0")],
    }
    p = tmp_path / "plan.json"
    p.write_text(json.dumps(plan))
    loaded = load_plan(p)
    assert loaded.shots[0].video_model == "seeddance-2.0"
    assert loaded.shots[0].previs_model == "gemini-3-pro-image-preview"


def test_characters_uppercased_and_deduped(tmp_path):
    raw = _shot_dict("EP001_SH01", 1)
    raw["asset_data"]["characters"] = ["jade", "JADE", "wren", "jade"]
    plan = {"episode_id": "ep_001", "project": "fixture", "shots": [raw]}
    p = tmp_path / "plan.json"
    p.write_text(json.dumps(plan))
    loaded = load_plan(p)
    assert [c.char_id for c in loaded.shots[0].characters] == ["JADE", "WREN"]


def test_real_ep001_plan_loads():
    """Verify against actual production plan."""
    real_path = (
        _REPO_ROOT
        / "projects"
        / "tartarus"
        / "state"
        / "visual"
        / "plans"
        / "ep_001_plan.json"
    )
    if not real_path.exists():
        pytest.skip("EP001 plan not available")
    plan = load_plan(real_path)
    assert len(plan.shots) == 37
    s0 = plan.shots[0]
    assert s0.shot_id == "EP001_SH01"
    assert s0.previs_model is not None  # legacy model field present
    assert s0.video_model is None  # not yet in production plans
