from __future__ import annotations

from pathlib import Path

import pytest

from recoil.pipeline.cli import generate
from recoil.pipeline.orchestrator.batch_selector import parse_batch_selector


PROJECT = "fixture"


@pytest.fixture(autouse=True)
def _projects_root(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
    root = tmp_path / "projects"
    root.mkdir()
    (root / ".recoil-data-root").touch()
    project_root = root / PROJECT
    project_root.mkdir()
    monkeypatch.setenv("RECOIL_PROJECTS_ROOT", str(root))
    return project_root


def _rederive_args(*extra: str) -> list[str]:
    return [
        "--project",
        PROJECT,
        "--episode",
        "1",
        *extra,
    ]


def test_rederive_from_script_requires_batch(
    capsys: pytest.CaptureFixture[str],
) -> None:
    with pytest.raises(SystemExit) as exc:
        generate._run_rederive_cli(_rederive_args("--from-script"))

    captured = capsys.readouterr()
    assert exc.value.code == 2
    assert "--from-script requires --batch" in captured.err


def test_rederive_batch_requires_from_script(
    capsys: pytest.CaptureFixture[str],
) -> None:
    with pytest.raises(SystemExit) as exc:
        generate._run_rederive_cli(_rederive_args("--batch", "EP001_CONT_001"))

    captured = capsys.readouterr()
    assert exc.value.code == 2
    assert "rederive --batch requires --from-script or --board-only" in captured.err


def test_rederive_from_script_rejects_malformed_batch_selector(
    capsys: pytest.CaptureFixture[str],
) -> None:
    with pytest.raises(SystemExit) as exc:
        generate._run_rederive_cli(
            _rederive_args("--from-script", "--batch", "BATCH_001")
        )

    captured = capsys.readouterr()
    assert exc.value.code == 2
    assert "invalid --batch selector 'BATCH_001'" in captured.err


def test_rederive_from_script_rejects_selector_episode_mismatch(
    capsys: pytest.CaptureFixture[str],
) -> None:
    with pytest.raises(SystemExit) as exc:
        generate._run_rederive_cli(
            _rederive_args("--from-script", "--batch", "EP002_CONT_001")
        )

    captured = capsys.readouterr()
    assert exc.value.code == 2
    assert "--batch episode EP002 != --episode 1" in captured.err


@pytest.mark.parametrize(
    "skip_flag",
    ["--skip-camera-test", "--skip-plan", "--skip-extract"],
)
def test_rederive_from_script_rejects_skip_flags(
    skip_flag: str,
    capsys: pytest.CaptureFixture[str],
) -> None:
    with pytest.raises(SystemExit) as exc:
        generate._run_rederive_cli(
            _rederive_args(
                "--from-script",
                "--batch",
                "EP001_CONT_001",
                skip_flag,
            )
        )

    captured = capsys.readouterr()
    assert exc.value.code == 2
    assert (
        "--from-script cannot be combined with --skip-* — it always heals "
        "from the live script"
    ) in captured.err


def test_rederive_from_script_routes_before_upstream_work(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    calls: list[tuple[str, int, object, dict]] = []

    def fake_from_script_rederive(project: str, episode: int, selector, **kwargs):
        calls.append((project, episode, selector, kwargs))
        return generate.EXIT_OK

    def fail_camera_test(self, episode_num: int):  # noqa: ANN001
        raise AssertionError("run_camera_test must not run for --from-script")

    def fail_storyboard_pass(self, episode: int, bible):  # noqa: ANN001
        raise AssertionError("run_storyboard_pass must not run for --from-script")

    monkeypatch.setattr(
        generate,
        "_run_from_script_rederive",
        fake_from_script_rederive,
    )
    monkeypatch.setattr(generate.IngestPipeline, "run_camera_test", fail_camera_test)
    monkeypatch.setattr(
        generate.IngestPipeline,
        "run_storyboard_pass",
        fail_storyboard_pass,
    )

    code = generate._run_rederive_cli(
        _rederive_args("--from-script", "--batch", "EP001_CONT_001")
    )

    assert code == generate.EXIT_OK
    assert calls == [
        (
            PROJECT,
            1,
            parse_batch_selector("EP001_CONT_001"),
            {"dry_run": False},
        )
    ]


def test_flat_batch_new_take_reroll_path_still_reachable(
    monkeypatch: pytest.MonkeyPatch,
    capsys: pytest.CaptureFixture[str],
) -> None:
    calls: list[dict] = []

    def fake_batch_reroll(**kwargs):
        calls.append(kwargs)
        return {"success": True, "sentinel": "flat-reroll"}

    monkeypatch.setattr(generate, "_run_batch_reroll", fake_batch_reroll)
    monkeypatch.setattr(
        generate.sys,
        "argv",
        [
            "generate.py",
            "--project",
            PROJECT,
            "--episode",
            "1",
            "--batch",
            "EP001_CONT_001",
            "--new-take",
        ],
    )

    code = generate.main()

    captured = capsys.readouterr()
    assert code == generate.EXIT_OK
    assert '"sentinel": "flat-reroll"' in captured.out
    assert calls == [
        {
            "project": PROJECT,
            "episode": 1,
            "batch": "EP001_CONT_001",
            "strategy": None,
            "seed": None,
            "make_primary": False,
            "budget_usd": 25.0,
            "dry_run": False,
        }
    ]
