"""Tests for filmstrip API boundary conditions (first/last shot prev/next)."""
import pytest


def _filmstrip_result(store, episode_id, shot_id):
    """Replicate the filmstrip logic from ReviewHandler._api_dailies_filmstrip.

    Returns the same dict structure the API would return, or raises ValueError
    on invalid input.
    """
    shots = store.get_shots_by_episode(episode_id)
    if not shots:
        raise ValueError(f"No shots found for {episode_id}")

    current_idx = None
    for i, s in enumerate(shots):
        if s["shot_id"] == shot_id:
            current_idx = i
            break

    if current_idx is None:
        raise ValueError(f"Shot {shot_id} not found in {episode_id}")

    def shot_frame(shot_dict):
        takes = shot_dict.get("takes", [])
        frame_path = shot_dict.get("output_path", "")
        if takes:
            for t in reversed(takes):
                if t.get("file_path") and not t.get("rejected"):
                    frame_path = t["file_path"]
                    break
        return {
            "shot_id": shot_dict["shot_id"],
            "status": shot_dict.get("status", ""),
            "frame_path": frame_path,
            "take_count": len(takes),
        }

    return {
        "episode_id": episode_id,
        "current": shot_frame(shots[current_idx]),
        "prev": shot_frame(shots[current_idx - 1]) if current_idx > 0 else None,
        "next": shot_frame(shots[current_idx + 1]) if current_idx < len(shots) - 1 else None,
    }


def _make_store(tmp_path, shots):
    """Create an ExecutionStore with the given shots inserted."""
    from recoil.execution.execution_store import ExecutionStore
    db_path = tmp_path / "filmstrip_test.db"
    store = ExecutionStore(project="test", db_path=db_path)
    for s in shots:
        store.insert_shot(s)
    return store


# ── Boundary tests ─────────────────────────────────────────────────


def test_first_shot_has_no_prev(tmp_path):
    """First shot in an episode should have prev=None."""
    store = _make_store(tmp_path, [
        {"shot_id": "EP001_SH001", "episode_id": "EP001", "status": "previs_pending"},
        {"shot_id": "EP001_SH002", "episode_id": "EP001", "status": "previs_pending"},
        {"shot_id": "EP001_SH003", "episode_id": "EP001", "status": "previs_pending"},
    ])

    result = _filmstrip_result(store, "EP001", "EP001_SH001")

    assert result["prev"] is None
    assert result["current"]["shot_id"] == "EP001_SH001"
    assert result["next"] is not None
    assert result["next"]["shot_id"] == "EP001_SH002"


def test_last_shot_has_no_next(tmp_path):
    """Last shot in an episode should have next=None."""
    store = _make_store(tmp_path, [
        {"shot_id": "EP001_SH001", "episode_id": "EP001", "status": "previs_pending"},
        {"shot_id": "EP001_SH002", "episode_id": "EP001", "status": "previs_pending"},
        {"shot_id": "EP001_SH003", "episode_id": "EP001", "status": "previs_pending"},
    ])

    result = _filmstrip_result(store, "EP001", "EP001_SH003")

    assert result["next"] is None
    assert result["current"]["shot_id"] == "EP001_SH003"
    assert result["prev"] is not None
    assert result["prev"]["shot_id"] == "EP001_SH002"


def test_single_shot_episode_has_no_prev_or_next(tmp_path):
    """An episode with only one shot should have both prev=None and next=None."""
    store = _make_store(tmp_path, [
        {"shot_id": "EP002_SH001", "episode_id": "EP002", "status": "previs_pending"},
    ])

    result = _filmstrip_result(store, "EP002", "EP002_SH001")

    assert result["prev"] is None
    assert result["next"] is None
    assert result["current"]["shot_id"] == "EP002_SH001"


def test_middle_shot_has_both_prev_and_next(tmp_path):
    """A middle shot should have both prev and next populated."""
    store = _make_store(tmp_path, [
        {"shot_id": "EP001_SH001", "episode_id": "EP001", "status": "previs_pending"},
        {"shot_id": "EP001_SH002", "episode_id": "EP001", "status": "previs_generated"},
        {"shot_id": "EP001_SH003", "episode_id": "EP001", "status": "previs_pending"},
    ])

    result = _filmstrip_result(store, "EP001", "EP001_SH002")

    assert result["prev"] is not None
    assert result["prev"]["shot_id"] == "EP001_SH001"
    assert result["current"]["shot_id"] == "EP001_SH002"
    assert result["next"] is not None
    assert result["next"]["shot_id"] == "EP001_SH003"


# ── Error cases ────────────────────────────────────────────────────


def test_empty_episode_raises(tmp_path):
    """Requesting filmstrip for an episode with no shots should raise."""
    store = _make_store(tmp_path, [])

    with pytest.raises(ValueError, match="No shots found"):
        _filmstrip_result(store, "EP099", "EP099_SH001")


def test_missing_shot_raises(tmp_path):
    """Requesting a shot_id that doesn't exist in the episode should raise."""
    store = _make_store(tmp_path, [
        {"shot_id": "EP001_SH001", "episode_id": "EP001", "status": "previs_pending"},
    ])

    with pytest.raises(ValueError, match="not found"):
        _filmstrip_result(store, "EP001", "EP001_SH999")


# ── Cross-episode isolation ────────────────────────────────────────


def test_filmstrip_only_sees_own_episode(tmp_path):
    """Shots from other episodes should not leak into the filmstrip."""
    store = _make_store(tmp_path, [
        {"shot_id": "EP001_SH001", "episode_id": "EP001", "status": "previs_pending"},
        {"shot_id": "EP001_SH002", "episode_id": "EP001", "status": "previs_pending"},
        {"shot_id": "EP002_SH001", "episode_id": "EP002", "status": "previs_pending"},
        {"shot_id": "EP002_SH002", "episode_id": "EP002", "status": "previs_pending"},
    ])

    # First shot of EP002 — prev should be None (not EP001's last shot)
    result = _filmstrip_result(store, "EP002", "EP002_SH001")
    assert result["prev"] is None
    assert result["next"]["shot_id"] == "EP002_SH002"

    # Last shot of EP001 — next should be None (not EP002's first shot)
    result = _filmstrip_result(store, "EP001", "EP001_SH002")
    assert result["next"] is None
    assert result["prev"]["shot_id"] == "EP001_SH001"


# ── Frame path resolution ──────────────────────────────────────────


def test_frame_path_uses_latest_non_rejected_take(tmp_path):
    """shot_frame() should prefer the latest non-rejected take's file_path."""
    from recoil.execution.execution_store import ExecutionStore
    db_path = tmp_path / "takes_test.db"
    store = ExecutionStore(project="test", db_path=db_path)

    store.insert_shot({
        "shot_id": "EP001_SH001",
        "episode_id": "EP001",
        "status": "previs_generated",
        "output_path": "/fallback/path.png",
    })
    # Append takes — first good, second rejected, third good
    store.update_shot("EP001_SH001", append_take={
        "take_id": "take_001", "file_path": "/take1.png",
    })
    store.update_shot("EP001_SH001", append_take={
        "take_id": "take_002", "file_path": "/take2.png", "rejected": True,
    })
    store.update_shot("EP001_SH001", append_take={
        "take_id": "take_003", "file_path": "/take3.png",
    })

    # Also insert a second shot so we can check the filmstrip
    store.insert_shot({
        "shot_id": "EP001_SH002",
        "episode_id": "EP001",
        "status": "previs_pending",
    })

    result = _filmstrip_result(store, "EP001", "EP001_SH002")
    # prev is SH001 — its frame_path should be take_003 (latest non-rejected)
    assert result["prev"]["frame_path"] == "/take3.png"
    assert result["prev"]["take_count"] == 3
