"""R2VMultiRunner — modality ``r2v_multi``.

Thin wrapper over StepRunner.execute_pass (coverage-pass generation via
SeedDance R2V). Registered in the CP-4 modality registry so overnight
dispatch flows through dispatch() with receipt + cost tracking.

Payload schema (extends video_i2v payload with multi-shot fields):
    {
        "shot_id": str (head shot ID, used as pass_id),
        "prompt": str (multi-shot prompt with [Xs-Ys] timestamps),
        "model": str (default "seeddance-2.0"),
        "duration": int (total duration, capped at model max),
        "aspect_ratio": str,
        "generate_audio": bool,
        "provider_hints": {"tier": str, "r2v_multi": True, "segment_count": int},
        "reference_images": list[str] (file paths),
        "segment_shot_ids": list[str] (per-segment shot IDs),
        "expected_segment_timestamps": list[tuple[float, float]],
    }
"""

from __future__ import annotations

from pathlib import Path

from recoil.pipeline.core.registry import MODALITY_R2V_MULTI, RunResult
from recoil.pipeline.core.runners._shared import (
    _failure_metadata_production as _failure_metadata,
    make_run_result_id,
)


class R2VMultiRunner:
    """ModalityRunner for ``r2v_multi`` — multi-shot coverage pass.

    Wraps StepRunner.execute_pass. Converts the dispatch payload dict
    into execute_pass kwargs and maps PassResult → RunResult.
    """

    modality: str = MODALITY_R2V_MULTI

    def __init__(self, step_runner) -> None:
        self._step_runner = step_runner

    def run(self, payload: dict) -> RunResult:
        shot_id = payload.get("shot_id")
        prompt = payload.get("prompt")
        model = payload.get("model", "seeddance-2.0")
        segment_shot_ids = payload.get("segment_shot_ids")
        timestamps = payload.get("expected_segment_timestamps")

        if not shot_id or not prompt:
            return RunResult(
                id=make_run_result_id(shot_id, MODALITY_R2V_MULTI),
                modality=MODALITY_R2V_MULTI,
                success=False,
                error=(
                    f"R2VMultiRunner payload missing required keys: "
                    f"shot_id={shot_id!r}, prompt={'...' if prompt else None!r}"
                ),
                metadata=_failure_metadata(),
            )
        if not segment_shot_ids or not timestamps:
            return RunResult(
                id=make_run_result_id(shot_id, MODALITY_R2V_MULTI),
                modality=MODALITY_R2V_MULTI,
                success=False,
                error=(
                    f"R2VMultiRunner payload missing r2v_multi fields: "
                    f"segment_shot_ids={segment_shot_ids!r}, "
                    f"expected_segment_timestamps={timestamps!r}"
                ),
                metadata=_failure_metadata(),
            )

        ref_images = list(payload.get("reference_images") or [])
        provider_hints = payload.get("provider_hints") or {}
        tier = provider_hints.get("tier")
        seed = provider_hints.get("seed")
        forced_take_number = payload.get("forced_take_number")
        grouping = payload.get("grouping")
        pass_counter = payload.get("pass_counter")
        if pass_counter is None and isinstance(grouping, dict):
            if grouping.get("strategy") == "coverage" and grouping.get("ordinal") is not None:
                pass_counter = int(grouping["ordinal"])
        from recoil.execution.step_runner import build_identity_gates_from_payload
        gates = build_identity_gates_from_payload(payload)

        try:
            pass_result = self._step_runner.execute_pass(
                pass_id=shot_id,
                prompt=prompt,
                reference_image_paths=ref_images,
                segment_shot_ids=segment_shot_ids,
                expected_segment_timestamps=timestamps,
                model=model,
                duration=payload.get("duration", 10),
                aspect_ratio=payload.get("aspect_ratio", "9:16"),
                gates=gates,
                on_status=payload.get("on_status"),
                tier=tier,
                generate_audio=payload.get("generate_audio", True),
                grouping=grouping,
                pass_counter=pass_counter,
                tag=payload.get("tag"),
                seed=seed,
                forced_take_number=forced_take_number,
                generation_config=payload.get("generation_config"),
                element_config=payload.get("element_config"),
            )
        except Exception as e:  # noqa: BLE001
            return RunResult(
                id=make_run_result_id(shot_id, MODALITY_R2V_MULTI),
                modality=MODALITY_R2V_MULTI,
                success=False,
                error=f"{type(e).__name__}: {e}",
                metadata=_failure_metadata(),
            )

        try:
            files_on_disk = 1 if (pass_result.video_path and Path(pass_result.video_path).exists()) else 0
        except (TypeError, ValueError):
            files_on_disk = 0

        return RunResult(
            id=make_run_result_id(shot_id, MODALITY_R2V_MULTI),
            modality=self.modality,
            output_path=pass_result.video_path,
            success=pass_result.success,
            error=pass_result.error,
            metadata={
                "final_state": "pass_complete" if pass_result.success else "pass_failed",
                "cost_usd": pass_result.cost_usd,
                "gate_verdict": getattr(pass_result, "gate_verdict", None),
                "take_index": pass_result.take_index,
                "model": pass_result.model,
                "pipeline": pass_result.pipeline,
                "expected_cuts": pass_result.expected_cuts,
                "detected_cuts": pass_result.detected_cuts,
                "segment_count": len(segment_shot_ids),
                "segment_shot_ids": segment_shot_ids,
                "files_on_disk": files_on_disk,
            },
        )
