from pathlib import Path

import pytest

from recoil.pipeline.core.persistence import (
    SceneLockedError,
    load_scene,
    save_scene,
    save_scene_guarded,
)
from recoil.pipeline.core.take import Scene


def _scene(
    scene_id: str,
    *,
    locked: bool = False,
    lock_reason: str | None = None,
    marker: str = "original",
) -> Scene:
    return Scene(
        scene_id=scene_id,
        scene_metadata={"marker": marker},
        locked=locked,
        lock_reason=lock_reason,
    )


def _scene_path(tmp_path: Path) -> Path:
    return tmp_path / "ep_001_BATCH_004.json"


def test_guarded_write_to_new_path_creates_file(tmp_path: Path) -> None:
    path = _scene_path(tmp_path)

    save_scene_guarded(_scene("BATCH_004", marker="new"), path)

    assert path.exists()
    assert load_scene(path).scene_metadata["marker"] == "new"


def test_guarded_write_over_unlocked_existing_scene_overwrites(
    tmp_path: Path,
) -> None:
    path = _scene_path(tmp_path)
    save_scene(_scene("BATCH_004", marker="old"), path)

    save_scene_guarded(_scene("BATCH_004", marker="replacement"), path)

    restored = load_scene(path)
    assert restored.locked is False
    assert restored.scene_metadata["marker"] == "replacement"


def test_guarded_write_over_locked_scene_without_force_raises_and_preserves_bytes(
    tmp_path: Path,
) -> None:
    path = _scene_path(tmp_path)
    save_scene(_scene("BATCH_004", locked=True, lock_reason="staged"), path)
    original_bytes = path.read_bytes()

    with pytest.raises(SceneLockedError) as excinfo:
        save_scene_guarded(_scene("BATCH_004", marker="replacement"), path)

    assert excinfo.value.batch_id == "BATCH_004"
    assert excinfo.value.lock_reason == "staged"
    assert path.read_bytes() == original_bytes
    assert list(tmp_path.glob("*.pre-force-*.bak")) == []


def test_raw_save_scene_still_overwrites_locked_scene(tmp_path: Path) -> None:
    path = _scene_path(tmp_path)
    save_scene(_scene("BATCH_004", locked=True, lock_reason="staged"), path)

    save_scene(_scene("BATCH_004", marker="raw-overwrite"), path)

    restored = load_scene(path)
    assert restored.locked is False
    assert restored.scene_metadata["marker"] == "raw-overwrite"
