"""Area #3 W4 — canary focused integration.

S12_Shot4 / S12_Shot13 / S19 fixture 기반 shot_director → cp
→ scene_context_loader caller 통합. W0 baseline 대비 W2/W3 후
결과 검증.

raw 비교 가능 case (instrumented replay) vs cp-only case 분리.
"""
import json
from pathlib import Path

import pytest


REPO_ROOT = Path(__file__).resolve().parents[2]
W0_JSONL = REPO_ROOT / "backend" / "tests" / "_audit_outputs" / "area_3_w0" / "mutation_inventory.jsonl"


def _load_w0_baseline() -> list[dict]:
    """Load W0 mutation_inventory.jsonl if exists (conditional artifact).

    If W0 artifact is committed, verify shape + compare baseline.
    If W0 artifact remains run-local, skip baseline comparison.
    """
    if not W0_JSONL.exists():
        pytest.skip(
            "W0 mutation_inventory.jsonl not present — run-local W0 artifact. "
            "Run W0.1-W0.3 manually for baseline comparison."
        )
    return [json.loads(ln) for ln in W0_JSONL.read_text().strip().split("\n") if ln]


def test_canary_S12_Shot4_post_W2_W3_raw_replay_available():
    """S12_Shot4 close-up gaze pattern: post-W2 mutation 0, raw replay
    baseline 일치 (successful replay case only).
    """
    baseline = _load_w0_baseline()
    s12_s4 = [r for r in baseline if r["scene_index"] == 12 and r["shot_index"] == 4]
    if not s12_s4:
        pytest.skip("S12_Shot4 not in W0 baseline")

    row = s12_s4[0]
    if row.get("raw_source") != "instrumented_replay" or row.get("replay_status") != "success":
        pytest.skip(
            "S12_Shot4 raw replay unavailable — cp-only baseline. "
            "Manual canary verify required (W4 closure checklist)."
        )

    # Successful replay: raw LLM emit must be present (non-null array)
    raw = row["raw_llm_visible_entity_ids"]
    assert raw is not None and isinstance(raw, list), (
        f"S12_Shot4 raw_llm_visible_entity_ids must be array on replay success, got {raw!r}"
    )
    # post-W2 production: visible_entity_ids unchanged from raw LLM emit
    # (mutation 권한 박탈 → raw == post_mutation 일치 의무)
    assert sorted(raw) == sorted(row["post_mutation_visible"]), (
        f"S12_Shot4 post-W2 mutation 의심: raw {raw} vs post-mutation "
        f"{row['post_mutation_visible']}. Area #3 W2 mutation 0 violation."
    )


def test_path_1_structured_blocking_still_raises():
    """Path 1 structured drift (OFFSCREEN_RE + character_angles match) →
    VisibleStagingDriftError raise (W3 후 blocking 유지).
    No W0 baseline dependency.
    """
    from app.modules.pipeline.shot_visibility import detect_offscreen_drift_structured

    drift = detect_offscreen_drift_structured(
        visible_ids=["C01", "C02"],
        camera_direction="캐릭터A is in frame while 캐릭터B remains off-screen.",
        character_angles=[
            {
                "character": "캐릭터A",
                "gaze_direction_kind": "looks_at_character",
                "gaze_target_id": "C02",
            },
        ],
        id_to_name={"C01": "캐릭터A", "C02": "캐릭터B"},
    )
    assert "C02" in drift, "Path 1 structured drift still detected post-W3"
    assert drift["C02"] == "캐릭터B"


def test_path_2_proximity_diagnostic_does_not_raise():
    """Path 2 proximity NL fallback → returns dict (no raise).
    Diagnostic only post-W3.
    """
    from app.modules.pipeline.shot_visibility import (
        detect_offscreen_drift_proximity_diagnostic,
    )

    # No raise expected even when drift candidates exist
    result = detect_offscreen_drift_proximity_diagnostic(
        visible_ids=["C01"],
        camera_direction="캐릭터A is off-camera in this shot.",
        id_to_name={"C01": "캐릭터A"},
    )
    # Function returns dict (no exception). Caller is responsible for log.
    assert isinstance(result, dict)


# S12_Shot13 (body-part possession) / S19 (directional) canary verify:
#   → manual closure checklist (W4 manual review).
#   Reason: cp-only baseline 한계 (instrumented replay 불가 시 raw 비교
#   skip). assertion 의무가 baseline 가용성에 따라 변함 — automated test
#   scope 안 들이지 X. W4.2 manual checklist 항목으로 처리.
