from __future__ import annotations

from pathlib import Path

import pytest

from recoil.core.paths import ProjectPaths
from recoil.pipeline._lib import board_builder as bb
from recoil.pipeline.core.persistence import save_scene, scene_path
from recoil.pipeline.core.receipts import GenerationReceipt
from recoil.pipeline.core.registry import MODALITY_STORYBOARD, RunResult
from recoil.pipeline.core.take import Beat, Scene


def _shot(n: int) -> dict:
    return {
        "shot_id": f"EP001_SH{n:02d}",
        "scene_index": 1,
        "duration_s": 1.0,
        "intent": f"Beat {n} action.",
        "asset_data": {"characters": [], "location_id": None},
        "spatial_data": {},
    }


@pytest.fixture()
def project_paths(tmp_path, monkeypatch):
    paths = ProjectPaths(project_root=tmp_path / "fixture_project")
    paths.project_root.mkdir(parents=True)
    monkeypatch.setattr(
        ProjectPaths,
        "for_project",
        classmethod(lambda cls, project=None: paths),
    )
    return paths


def _write_batch_scene(shots: list[dict]) -> Path:
    beat = Beat(
        beat_id="BATCH_004",
        beat_metadata={
            "scene_id": "BATCH_004",
            "modality": "r2v_multi",
            "shot": shots[0],
            "batch_shots": shots,
            "batch_summary": {"shared_characters": [], "shared_location_id": None},
        },
    )
    scene = Scene(
        scene_id="BATCH_004",
        beats=[beat],
        scene_metadata={"episode": "ep_001", "project": "fixture_project"},
    )
    path = scene_path("fixture_project", "ep_001", "BATCH_004")
    save_scene(scene, path)
    return path


def _settings_passthrough(segments, **_kwargs):
    return [dict(segment, setting=f"Setting {i}") for i, segment in enumerate(segments, start=1)]


def _receipt(success: bool = True) -> GenerationReceipt:
    return GenerationReceipt(
        receipt_id="rcpt_test",
        modality=MODALITY_STORYBOARD,
        caller_id="board_builder",
        project="fixture_project",
        episode=1,
        shot_id="EP001_CONT_004",
        timestamp_utc="2026-06-11T00:00:00Z",
        run_result=RunResult(
            id="run_test",
            modality=MODALITY_STORYBOARD,
            output_path="/tmp/board.png" if success else None,
            metadata={},
            success=success,
            error=None,
        ),
    )


def _capture_dispatch(monkeypatch) -> dict:
    captured: dict = {}

    def fake_dispatch(modality, payload, *, context):
        captured["modality"] = modality
        captured["payload"] = payload
        captured["context"] = context
        return _receipt()

    monkeypatch.setattr(bb, "dispatch", fake_dispatch)
    return captured


def _run_board(monkeypatch, shots: list[dict], config: dict) -> dict:
    _write_batch_scene(shots)
    monkeypatch.setattr(bb, "derive_settings", _settings_passthrough)
    monkeypatch.setattr(bb.core_paths, "get_pipeline_config", lambda: config)
    captured = _capture_dispatch(monkeypatch)

    result = bb.build_and_dispatch_board(
        "fixture_project",
        1,
        "EP001_CONT_004",
        step_runner=object(),
    )

    assert result["success"] is True
    assert captured["modality"] == MODALITY_STORYBOARD
    return captured["payload"]


def test_half_high_tier_dispatches_halved_six_slot_payload_and_sidecar(
    project_paths,
    monkeypatch,
):
    payload = _run_board(
        monkeypatch,
        [_shot(i) for i in range(10, 15)],
        {"storyboard_iteration": {"quality": "high", "size": "half"}},
    )

    assert payload["quality"] == "high"
    assert payload["size_override"] == "720x1920"
    assert payload["sidecar_extra"]["iteration_tier"] == {
        "quality": "high",
        "size": "half",
        "size_override": "720x1920",
    }


def test_half_high_tier_dispatches_halved_four_slot_payload(project_paths, monkeypatch):
    payload = _run_board(
        monkeypatch,
        [_shot(i) for i in range(10, 13)],
        {"storyboard_iteration": {"quality": "high", "size": "half"}},
    )

    assert payload["quality"] == "high"
    assert payload["size_override"] == "864x1536"


def test_full_tier_dispatches_full_six_slot_payload(project_paths, monkeypatch):
    payload = _run_board(
        monkeypatch,
        [_shot(i) for i in range(10, 15)],
        {"storyboard_iteration": {"quality": "medium", "size": "full"}},
    )

    assert payload["quality"] == "medium"
    assert payload["size_override"] == "1440x3840"


def test_missing_tier_defaults_to_today_values(project_paths, monkeypatch):
    payload = _run_board(
        monkeypatch,
        [_shot(i) for i in range(10, 15)],
        {},
    )

    assert payload["quality"] == "high"
    assert payload["size_override"] == "1440x3840"
    assert payload["sidecar_extra"]["iteration_tier"] == {
        "quality": "high",
        "size": "full",
        "size_override": "1440x3840",
    }


def test_junk_quality_fails_loud(project_paths, monkeypatch):
    _write_batch_scene([_shot(i) for i in range(10, 15)])
    monkeypatch.setattr(bb, "derive_settings", _settings_passthrough)
    monkeypatch.setattr(
        bb.core_paths,
        "get_pipeline_config",
        lambda: {"storyboard_iteration": {"quality": "ultra", "size": "half"}},
    )
    _capture_dispatch(monkeypatch)

    with pytest.raises(bb.BoardBuilderError, match="storyboard_iteration.quality"):
        bb.build_and_dispatch_board(
            "fixture_project",
            1,
            "EP001_CONT_004",
            step_runner=object(),
        )
