"""Tests for the morning QC re-check script."""
import pytest
from unittest.mock import MagicMock, patch


def test_recheck_walks_pending_qc_shots():
    """The script must list all shots in pending_qc state."""
    from tools.recheck_pending_qc import find_pending_qc_shots

    fake_store = MagicMock()
    fake_store.get_all_shots.return_value = [
        {"shot_id": "EP001_SH01", "status": "video_complete"},
        {"shot_id": "EP001_SH02", "status": "pending_qc", "output_path": "video/v.mp4"},
        {"shot_id": "EP001_SH03", "status": "pending_qc", "output_path": "frames/k.png"},
        {"shot_id": "EP001_SH04", "status": "keyframe_generated"},
    ]
    pending = find_pending_qc_shots(fake_store)
    assert len(pending) == 2
    assert pending[0]["shot_id"] == "EP001_SH02"
    assert pending[1]["shot_id"] == "EP001_SH03"


def test_recheck_promotes_passing_shot():
    """If re-check passes, shot is promoted to its appropriate terminal state."""
    from tools.recheck_pending_qc import recheck_shot
    from recoil.core.critic import Outcome, CriticResult

    fake_store = MagicMock()
    fake_store.project = "test_project"
    pass_result = CriticResult(critic_name="video_frame", outcome=Outcome.PASS)

    with patch("tools.recheck_pending_qc._run_critic_for_shot",
               return_value=pass_result):
        outcome = recheck_shot(
            fake_store,
            {"shot_id": "EP001_SH02", "status": "pending_qc",
             "output_path": "video/v.mp4", "pipeline": "video"},
            project="test_project",
        )

    assert outcome == "promoted"
    fake_store.update_shot.assert_called_with("EP001_SH02", status="video_complete")


def test_recheck_marks_review_on_persistent_error():
    """If re-check still ERRORs, mark as needs_review."""
    from tools.recheck_pending_qc import recheck_shot
    from recoil.core.critic import Outcome, CriticResult

    fake_store = MagicMock()
    fake_store.project = "test_project"
    err_result = CriticResult(critic_name="video_frame", outcome=Outcome.ERROR, error="still down")

    with patch("tools.recheck_pending_qc._run_critic_for_shot",
               return_value=err_result):
        outcome = recheck_shot(
            fake_store,
            {"shot_id": "EP001_SH02", "status": "pending_qc",
             "output_path": "video/v.mp4", "pipeline": "video"},
            project="test_project",
        )

    assert outcome == "needs_review"
    last_call = fake_store.update_shot.call_args
    assert last_call.kwargs.get("status") == "needs_review"
    assert "still down" in last_call.kwargs.get("error_message", "")


def test_recheck_marks_failed_on_real_failure():
    """If re-check returns FAIL with hard failures, mark as needs_review for human action."""
    from tools.recheck_pending_qc import recheck_shot
    from recoil.core.critic import Outcome, CriticResult, Dimension, Severity

    fake_store = MagicMock()
    fake_store.project = "test_project"
    fail_result = CriticResult(
        critic_name="video_frame",
        outcome=Outcome.FAIL,
        dimensions=[Dimension(name="EXTRA_LIMBS", severity=Severity.HARD,
                              passed=False, message="3 arms in frames 2,3")],
    )

    with patch("tools.recheck_pending_qc._run_critic_for_shot",
               return_value=fail_result):
        outcome = recheck_shot(
            fake_store,
            {"shot_id": "EP001_SH02", "status": "pending_qc",
             "output_path": "video/v.mp4", "pipeline": "video"},
            project="test_project",
        )

    assert outcome == "failed"


def test_recheck_uses_character_context_for_start_frame(tmp_path):
    """The recheck script must plumb character context into StartFrameCritic
    so re-check doesn't re-introduce the silent identity bypass (Gemini Finding 2)."""
    from tools.recheck_pending_qc import _build_start_frame_critic_with_context

    shot_with_context = {
        "shot_id": "EP001_SH02",
        "status": "pending_qc",
        "inputs_snapshot": {
            "characters": [
                {"display_name": "Alice", "hair": "red", "clothing": "leather jacket"},
            ],
            "scene_elements": ["window", "laptop"],
            "identity_anchor": "Alice in her late 20s",
        },
    }
    critic = _build_start_frame_critic_with_context(shot_with_context)
    # Verify the critic was constructed WITH character descriptions
    assert len(critic.character_descriptions) == 1
    assert critic.character_descriptions[0]["name"] == "Alice"
    assert critic.character_descriptions[0]["hair"] == "red"
    assert "window" in critic.expected_elements
    assert "laptop" in critic.expected_elements
