from types import SimpleNamespace
from pathlib import Path
from unittest.mock import patch

from recoil.core.naming import build_filename
from recoil.execution.step_runner import StepRunner
from recoil.execution.types import GenerationResult
from recoil.pipeline.core.runners.r2v_multi_runner import R2VMultiRunner


def test_r2v_multi_actual_saved_filename_uses_grouping_identity(tmp_path):
    video_dir = tmp_path / "renders"
    video_dir.mkdir()
    paths = SimpleNamespace(project="fixture", project_root=tmp_path, video_dir=video_dir)
    step_runner = StepRunner(store=SimpleNamespace(), paths=paths)
    runner = R2VMultiRunner(step_runner)

    class FakeVideoModelClient:
        def __init__(self, *args, **kwargs):
            pass

        def submit(self, payload):
            return SimpleNamespace(result=None)

        def wait_for_job(self, job, timeout_s, on_status=None):
            return GenerationResult(
                success=True,
                video_data=b"mp4",
                model="seeddance-2.0",
                cost=0.1,
                metadata={},
            )

    payload = {
        "shot_id": "BATCH_002",
        "prompt": "test prompt",
        "model": "seeddance-2.0",
        "duration": 4,
        "aspect_ratio": "9:16",
        "generate_audio": False,
        "reference_images": [],
        "segment_shot_ids": ["EP001_SH01", "EP001_SH02"],
        "expected_segment_timestamps": [(0.0, 2.0), (2.0, 4.0)],
        "grouping": {
            "strategy": "continuity",
            "ordinal": 2,
            "shot_ids": ["EP001_SH01", "EP001_SH02"],
            "source_pass_id": None,
        },
        "generation_config": {"tier": "pro", "seed": 1234},
        "element_config": {"identity_ref_mode": "full_turnaround"},
    }
    sidecars = []

    def _capture_sidecar(*args, **kwargs):
        sidecars.append(kwargs["unified_payload"])

    with (
        patch(
            "recoil.execution.video_model_client.VideoModelClient",
            FakeVideoModelClient,
        ),
        patch.object(step_runner, "_write_sidecar", _capture_sidecar),
        patch.object(step_runner, "_extract_boundary_frames", lambda *args, **kwargs: []),
    ):
        result = runner.run(payload)

    assert result.success is True
    assert result.output_path is not None
    assert "_CONT_002_" in result.output_path
    assert Path(result.output_path).exists()
    assert sidecars[0]["grouping"] == payload["grouping"]
    assert sidecars[0]["generation_config"] == payload["generation_config"]
    assert sidecars[0]["element_config"] == payload["element_config"]


def test_r2v_multi_live_writer_scan_preserves_take1_and_creates_take2(tmp_path):
    video_dir = tmp_path / "renders"
    video_dir.mkdir()
    paths = SimpleNamespace(
        project="fixture",
        project_root=tmp_path,
        video_dir=video_dir,
    )
    step_runner = StepRunner(store=SimpleNamespace(), paths=paths)
    runner = R2VMultiRunner(step_runner)
    grouping = {
        "strategy": "continuity",
        "ordinal": 5,
        "shot_ids": ["EP001_SH01", "EP001_SH02"],
        "source_pass_id": None,
    }
    take1 = video_dir / build_filename(
        episode=1,
        strategy="continuity",
        ordinal=5,
        shot_ids=grouping["shot_ids"],
        take=1,
    )
    take1.write_bytes(b"original take1")

    class FakeVideoModelClient:
        def __init__(self, *args, **kwargs):
            pass

        def submit(self, payload):
            return SimpleNamespace(result=None)

        def wait_for_job(self, job, timeout_s, on_status=None):
            return GenerationResult(
                success=True,
                video_data=b"new take2",
                model="seeddance-2.0",
                cost=0.1,
                metadata={},
            )

    payload = {
        "shot_id": "BATCH_005",
        "prompt": "test prompt",
        "model": "seeddance-2.0",
        "duration": 4,
        "aspect_ratio": "9:16",
        "generate_audio": False,
        "reference_images": [],
        "segment_shot_ids": list(grouping["shot_ids"]),
        "expected_segment_timestamps": [(0.0, 2.0), (2.0, 4.0)],
        "grouping": dict(grouping),
    }

    with (
        patch(
            "recoil.execution.video_model_client.VideoModelClient",
            FakeVideoModelClient,
        ),
        patch.object(
            step_runner,
            "_extract_boundary_frames",
            lambda *args, **kwargs: [],
        ),
    ):
        result = runner.run(payload)

    take2 = video_dir / build_filename(
        episode=1,
        strategy="continuity",
        ordinal=5,
        shot_ids=grouping["shot_ids"],
        take=2,
    )

    assert result.success is True
    assert take1.read_bytes() == b"original take1"
    assert take2.read_bytes() == b"new take2"
    assert result.output_path == str(take2)


def test_failed_pass_does_not_inherit_estimated_cost(tmp_path):
    """A failed r2v pass must report the provider's actual cost (0 for a
    pre-inference rejection), NOT the upfront estimate (REC-122 phantom
    spend: billing-rejected Flora runs were stamped with full estimates)."""
    video_dir = tmp_path / "renders"
    video_dir.mkdir()
    paths = SimpleNamespace(
        project="fixture", project_root=tmp_path, video_dir=video_dir
    )
    step_runner = StepRunner(store=SimpleNamespace(), paths=paths)
    runner = R2VMultiRunner(step_runner)

    class FakeVideoModelClient:
        def __init__(self, *args, **kwargs):
            pass

        def submit(self, payload):
            return SimpleNamespace(result=None)

        def wait_for_job(self, job, timeout_s, on_status=None):
            return GenerationResult(
                success=False,
                model="seeddance-2.0",
                cost=0.0,
                error="flora: BILLING_NOT_ENOUGH_CREDITS",
            )

    payload = {
        "shot_id": "BATCH_007",
        "prompt": "test prompt",
        "model": "seeddance-2.0",
        "duration": 15,
        "aspect_ratio": "9:16",
        "generate_audio": False,
        "reference_images": [],
        "segment_shot_ids": ["EP001_SH22", "EP001_SH23"],
        "expected_segment_timestamps": [(0.0, 7.0), (7.0, 15.0)],
        "grouping": {
            "strategy": "continuity",
            "ordinal": 7,
            "shot_ids": ["EP001_SH22", "EP001_SH23"],
            "source_pass_id": None,
        },
    }
    with patch(
        "recoil.execution.video_model_client.VideoModelClient",
        FakeVideoModelClient,
    ):
        result = runner.run(payload)

    assert result.success is False
    assert float((result.metadata or {}).get("cost_usd", -1)) == 0.0
    assert "BILLING_NOT_ENOUGH_CREDITS" in (result.error or "")
