from __future__ import annotations

import subprocess
import sys

import pytest

from recoil.pipeline.orchestrator.batch_selector import (
    SelectorResolutionError,
    resolve,
)


def _make_resolver_fixture(tmp_path, monkeypatch):
    projects_root = tmp_path / "projects"
    projects_root.mkdir()
    (projects_root / ".recoil-data-root").write_text("recoil-data-root\n")
    project_root = projects_root / "fixture"
    project_root.mkdir()
    (project_root / "project_config.json").write_text("{}")
    monkeypatch.setenv("RECOIL_PROJECTS_ROOT", str(projects_root))

    scenes_dir = project_root / "_pipeline" / "state" / "orchestration" / "scenes"
    scenes_dir.mkdir(parents=True)
    (scenes_dir / "ep_001_BATCH_001.json").write_text("{}")
    (scenes_dir / "1_BATCH_001.json").write_text("{}")
    (scenes_dir / "ep_001_BATCH_001.json.pre-x.bak").touch()
    (scenes_dir / "ep_001_BATCH_001__ab_runA_x.json").touch()
    return scenes_dir


def test_resolve_controlled_fixture_pins_canonical_scene(tmp_path, monkeypatch):
    _make_resolver_fixture(tmp_path, monkeypatch)

    ref = resolve("EP001_CONT_001", "fixture")

    assert ref.scene_path.name == "ep_001_BATCH_001.json"
    assert ref.selector == "EP001_CONT_001"
    assert ref.scene_id == "BATCH_001"
    assert ref.episode == 1
    assert ref.strategy == "continuity"
    assert ref.ordinal == 1
    assert ref.project == "fixture"
    assert not ref.scene_path.name.endswith(".bak")
    assert "__ab_runA_" not in ref.scene_path.name
    assert resolve("EP001_CONT_001", "FIXTURE").project == "fixture"


def test_resolve_invalid_selector_raises_typed_error(tmp_path, monkeypatch):
    _make_resolver_fixture(tmp_path, monkeypatch)

    with pytest.raises(SelectorResolutionError):
        resolve("garbage", "fixture")


def test_resolve_missing_canonical_scene_raises_file_not_found(tmp_path, monkeypatch):
    _make_resolver_fixture(tmp_path, monkeypatch)

    with pytest.raises(FileNotFoundError):
        resolve("EP999_CONT_001", "fixture")


def _tartarus_ep001_available() -> bool:
    try:
        resolve("EP001_CONT_001", "tartarus")
        return True
    except Exception:
        return False


@pytest.mark.skipif(
    not _tartarus_ep001_available(),
    reason="live tartarus EP001 unavailable in this environment (hermetic CI)",
)
def test_resolve_live_tartarus_smoke_probe():
    ref = resolve("EP001_CONT_001", "tartarus")

    assert ref.scene_path.name.startswith("ep_")
    assert not ref.scene_path.name.endswith(".bak")
    assert "__ab_runA_" not in ref.scene_path.name
    assert ref.scene_path.exists()


def test_resolve_import_does_not_drag_in_episode_runner():
    result = subprocess.run(
        [
            sys.executable,
            "-c",
            (
                "import recoil.pipeline.orchestrator.batch_selector, sys; "
                "assert 'recoil.pipeline.orchestrator.episode_runner' "
                "not in sys.modules, "
                "'batch_selector still drags in episode_runner at import'"
            ),
        ],
        capture_output=True,
        text=True,
    )

    assert result.returncode == 0, result.stderr
