"""close × ref_usage matrix v1 — scene_reference_service.build_prev_shot_background_ref tests.

Spec §4.9: framing_scale=close + ref_usage != zoom_in_detail → RefContractError.
best_prev_bytes 존재 + attach 후보 있을 때만 enforce (early return 후).
"""
import pytest

from app.core.framing_scale import (
    FRAMING_CLOSE,
    FRAMING_MEDIUM,
    FRAMING_WIDE,
)
from app.core.ref_contract_validator import RefContractError
from app.services.scene_reference_service import SceneReferenceService


def _make_svc():
    """Synthetic SceneReferenceService — minimal setup.

    Constructor signature: __init__(self, db: OrmSession, project_id: str).
    build_prev_shot_background_ref 는 본 test path 에서 DB 직접 사용 X
    (best_prev_bytes + dep_detail_map + staging argument 만 read), 따라서
    db=MagicMock() 로 충분. db.query() etc. 미호출 verified.
    """
    from unittest.mock import MagicMock
    return SceneReferenceService(db=MagicMock(), project_id="p_test")


def _make_still_data(scene_index=1, shot_index=2):
    return {
        "scene_index": scene_index,
        "shot_index": shot_index,
        "visible_entities_json": "[]",
    }


def _make_staging(framing_scale):
    return {"framing_scale": framing_scale, "camera_direction": "wide"}


def _make_dep_detail_map(scene_idx, shot_idx, ref_usage):
    key = f"{scene_idx}_{shot_idx}"
    return {key: {"ref_usage": ref_usage, "ignore_elements": "", "keep_elements": []}}


def test_close_zoom_in_detail_allow():
    """close + zoom_in_detail = allow (label 정상 생성).

    2026-05-15 zoom_in_detail source provenance hardening — Layer 3 통과 위해
    bytes_source_kind="dep_scene" + dep_scene_id + matching stills 명시 의무.
    """
    svc = _make_svc()
    still = _make_still_data(1, 2)
    staging = _make_staging(FRAMING_CLOSE)
    dep_detail_map = _make_dep_detail_map(1, 2, "zoom_in_detail")

    result = svc.build_prev_shot_background_ref(
        best_prev_bytes=b"\x89PNG fake bytes",
        bytes_source_kind="dep_scene",  # spec §6 — zoom_in_detail Layer 3 통과 위해 dep_scene
        still_data=still,
        visible_entities=[],
        current_location_ids=[],
        dep_scene_id="S0_Shot1",  # Layer 2.5 통과 의무
        stills=[{"id": "S0_Shot1", "visible_entities_json": "[]"}],  # §4.5 selection
        location_scene_history={},
        dep_detail_map=dep_detail_map,
        staging=staging,
        state_variant_sids=set(),
        entity_lookup={},
    )
    assert result is not None
    label, _bytes, _loc_id, _ref_role, _ref_role_metadata = result  # Area #11 v1 W3: 5-tuple
    assert "SAME FRAME ZOOMED" in label  # zoom_in_detail label content


def test_close_exact_background_raises():
    svc = _make_svc()
    still = _make_still_data(1, 2)
    staging = _make_staging(FRAMING_CLOSE)
    dep_detail_map = _make_dep_detail_map(1, 2, "exact_background")

    with pytest.raises(RefContractError) as excinfo:
        svc.build_prev_shot_background_ref(
            best_prev_bytes=b"\x89PNG fake bytes",
            bytes_source_kind="location_history",  # spec §6 — exact_background = Layer 3 skip, Layer 4 close raise
            still_data=still,
            visible_entities=[],
            current_location_ids=["loc1"],  # Layer 2.6 통과 의무
            dep_scene_id=None,
            stills=[],
            location_scene_history={"loc1": (None, {"id": "S0", "visible_entities_json": "[]"})},
            dep_detail_map=dep_detail_map,
            staging=staging,
            state_variant_sids=set(),
            entity_lookup={},
        )
    assert "close_ref_usage_violation" in str(excinfo.value)
    assert "exact_background" in str(excinfo.value)


def test_close_atmosphere_reference_raises():
    svc = _make_svc()
    still = _make_still_data(1, 2)
    staging = _make_staging(FRAMING_CLOSE)
    dep_detail_map = _make_dep_detail_map(1, 2, "atmosphere_reference")

    with pytest.raises(RefContractError) as excinfo:
        svc.build_prev_shot_background_ref(
            best_prev_bytes=b"\x89PNG fake bytes",
            bytes_source_kind="location_history",
            still_data=still,
            visible_entities=[],
            current_location_ids=["loc1"],
            dep_scene_id=None,
            stills=[],
            location_scene_history={"loc1": (None, {"id": "S0", "visible_entities_json": "[]"})},
            dep_detail_map=dep_detail_map,
            staging=staging,
            state_variant_sids=set(),
            entity_lookup={},
        )
    assert "close_ref_usage_violation" in str(excinfo.value)
    assert "atmosphere_reference" in str(excinfo.value)


def test_close_no_dep_entry_returns_none():
    """close + dep_detail_map entry 부재(location_refs=[]) → return None.

    FINDING 9 W3 (Cat1 sub-cause B): dep_detail_map 에 shot key 가 없는 것은
    shot_dependency_t2i 가 location_refs=[] 를 emit 한 producer 의 정상 verdict.
    "location_ref object 존재 + ref_usage 빈 문자열"(malformed) 과 다른 상태 —
    close shot 이 declared location_ref 가 없으면 best_prev_bytes 로 prev-shot
    background 를 합성하지 않고 return None (matrix false-positive 제거). 실제
    declared ref 의 close×ref_usage matrix invariant 는 보존.

    (W3 이전 본 test 는 close_ref_usage_violation raise 를 단언했으나, 그것이
    바로 W3 가 정정한 false-positive 다.)
    """
    svc = _make_svc()
    still = _make_still_data(1, 2)
    staging = _make_staging(FRAMING_CLOSE)
    dep_detail_map = {}  # no entry → location_refs=[] (declared no-ref)

    result = svc.build_prev_shot_background_ref(
        best_prev_bytes=b"\x89PNG fake bytes",
        bytes_source_kind="location_history",
        still_data=still,
        visible_entities=[],
        current_location_ids=["loc1"],
        dep_scene_id=None,
        stills=[],
        location_scene_history={"loc1": (None, {"id": "S0", "visible_entities_json": "[]"})},
        dep_detail_map=dep_detail_map,
        staging=staging,
        state_variant_sids=set(),
        entity_lookup={},
    )
    assert result is None


def test_close_no_prev_bytes_early_return_no_matrix_check():
    """close + best_prev_bytes=None → early return None, matrix not enforced."""
    svc = _make_svc()
    still = _make_still_data(1, 2)
    staging = _make_staging(FRAMING_CLOSE)
    # dep_detail_map 가 exact_background 라도 best_prev_bytes None 면 early return
    dep_detail_map = _make_dep_detail_map(1, 2, "exact_background")

    result = svc.build_prev_shot_background_ref(
        best_prev_bytes=None,  # ← early return trigger
        bytes_source_kind="none",  # spec §5 — bytes None 시 "none" enum
        still_data=still,
        visible_entities=[],
        current_location_ids=[],
        dep_scene_id=None,
        stills=[],
        location_scene_history={},
        dep_detail_map=dep_detail_map,
        staging=staging,
        state_variant_sids=set(),
        entity_lookup={},
    )
    assert result is None  # early return, matrix not enforced


def test_medium_any_ref_usage_allow():
    """framing_scale=medium 일 때 ref_usage 어떤 값이든 raise 안 함 (matrix close 만).

    2026-05-15 zoom_in_detail source provenance hardening — 4 ref_usage 모두
    Layer 3/2.6 통과 위해 bytes_source_kind="dep_scene" + dep fixture. 본 test
    의 원래 의도 (close 만 matrix v1 enforce) 보존.
    """
    svc = _make_svc()
    still = _make_still_data(1, 2)

    for ref_usage in ("zoom_in_detail", "exact_background", "atmosphere_reference", ""):
        staging = _make_staging(FRAMING_MEDIUM)
        dep_detail_map = _make_dep_detail_map(1, 2, ref_usage)

        # raise 안 해야 — close 만 matrix enforce
        try:
            svc.build_prev_shot_background_ref(
                best_prev_bytes=b"\x89PNG fake bytes",
                bytes_source_kind="dep_scene",  # spec §6 — Layer 3 통과 위해 dep_scene
                still_data=still,
                visible_entities=[],
                current_location_ids=[],
                dep_scene_id="S0_Shot1",  # Layer 2.5 통과
                stills=[{"id": "S0_Shot1", "visible_entities_json": "[]"}],  # §4.5 selection
                location_scene_history={},
                dep_detail_map=dep_detail_map,
                staging=staging,
                state_variant_sids=set(),
                entity_lookup={},
            )
        except RefContractError:
            pytest.fail(f"medium + {ref_usage!r} 가 RefContractError raise — close 만 enforce 의무")
