"""Phase 0 — smoke tests for load_plan + CanonicalShot."""

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, CanonicalPlan  # noqa: E402


def _flat_plan_dict() -> dict:
    return {
        "episode_id": "ep_001",
        "project": "fixture",
        "shots": [
            {
                "shot_id": "EP001_SH01",
                "scene_index": 1,
                "pipeline": "still",
                "model": "gemini-3-pro-image-preview",
                "routing_data": {"target_editorial_duration_s": 3, "is_env_only": True},
                "asset_data": {"location_id": "L1", "characters": ["jade"]},
                "prompt_data": {"shot_type": "WS"},
            },
        ],
    }


def _sequences_plan_dict() -> dict:
    return {
        "episode_id": "ep_002",
        "project": "fixture",
        "sequences": {
            "SEQ_001": [
                {
                    "shot_id": "EP002_SH01",
                    "scene_index": 1,
                    "pipeline": "still",
                    "model": "gemini-3-pro-image-preview",
                    "routing_data": {"target_editorial_duration_s": 4},
                    "asset_data": {"location_id": "L1", "characters": []},
                    "prompt_data": {"shot_type": "MS"},
                },
            ],
        },
    }


def test_flat_plan_loads(tmp_path):
    p = tmp_path / "ep_001_plan.json"
    p.write_text(json.dumps(_flat_plan_dict()))
    plan = load_plan(p)
    assert isinstance(plan, CanonicalPlan)
    assert plan.episode_id == "ep_001"
    assert len(plan.shots) == 1
    s = plan.shots[0]
    assert s.shot_id == "EP001_SH01"
    assert s.sequence_id is None
    assert s.previs_model == "gemini-3-pro-image-preview"
    assert s.video_model is None
    assert [c.char_id for c in s.characters] == ["JADE"]


def test_sequences_plan_loads(tmp_path):
    p = tmp_path / "ep_002_plan.json"
    p.write_text(json.dumps(_sequences_plan_dict()))
    plan = load_plan(p)
    assert len(plan.shots) == 1
    s = plan.shots[0]
    assert s.sequence_id == "SEQ_001"
    assert s.shot_id == "EP002_SH01"


def test_missing_file_raises(tmp_path):
    with pytest.raises(FileNotFoundError):
        load_plan(tmp_path / "nope.json")


def test_neither_shape_raises(tmp_path):
    p = tmp_path / "bad.json"
    p.write_text(json.dumps({"episode_id": "x", "project": "y"}))
    with pytest.raises(ValueError):
        load_plan(p)


def test_video_model_field_passes_through(tmp_path):
    raw = _flat_plan_dict()
    raw["shots"][0]["video_model"] = "seeddance-2.0"
    p = tmp_path / "plan.json"
    p.write_text(json.dumps(raw))
    plan = load_plan(p)
    assert plan.shots[0].video_model == "seeddance-2.0"


# ---------------------------------------------------------------------------
# Phase 4 — CanonicalShot.quality field + gpt-image-2 canonicalization
# ---------------------------------------------------------------------------

from recoil.pipeline._lib.plan_loader import CanonicalShot, _canonicalize_shot  # noqa: E402


def test_canonical_shot_quality_field_present():
    """The new field exists on the dataclass and defaults to None."""
    assert "quality" in CanonicalShot.__dataclass_fields__
    cs = CanonicalShot(
        shot_id="x",
        scene_index=0,
        sequence_id=None,
        pipeline=None,
        previs_model=None,
        video_model=None,
        location_id=None,
        characters=[],
        shot_type=None,
        duration_s=None,
        is_env_only=False,
        has_dialogue=False,
        aspect_ratio=None,
        raw={},
    )
    assert cs.quality is None


def test_canonical_shot_quality_none_for_legacy():
    """Plans without a 'quality' field canonicalize to quality=None."""
    s = _canonicalize_shot(
        {
            "shot_id": "shot_002",
            "model": "seedream-v4.5",
        },
        sequence_id=None,
    )
    assert s.quality is None


def test_gpt_image_2_model_canonicalized_via_previs_model():
    """gpt-image-2 routes through CanonicalShot.previs_model (no new field)
    and the per-shot quality canonicalizes from 'quality'."""
    s = _canonicalize_shot(
        {
            "shot_id": "shot_001",
            "model": "gpt-image-2",
            "quality": "low",
        },
        sequence_id=None,
    )
    assert s.previs_model == "gpt-image-2"
    assert s.quality == "low"
