"""_compute_forward_zoom_targets — forward-look continuity helper 단위 테스트.

결함 A (forward-look continuity) fix의 회귀 가드. shot_dependency_t2i schema의
실제 키(`scene_index`/`shot_index`, 접두사 없음)로 reverse-lookup 정확성 검증.

이전 버전은 `ref_scene_index`/`ref_shot_index` (잘못된 접두사) 사용으로 dead
code였음 — Claude/Codex 듀얼 리뷰에서 발견.

시나리오 의존 0 — generic placeholder (scene_index, shot_index, location_id) 만
사용.
"""
from __future__ import annotations

import pytest

from app.core.steps.detail_steps import _compute_forward_zoom_targets


def test_zoom_target_match_basic():
    """후속 shot이 (si, shot_idx) 를 zoom_in_detail 로 가리키면 매칭."""
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,  # 후속
            "location_refs": [
                {
                    "scene_index": 5,
                    "shot_index": 3,  # 앞 shot
                    "ref_usage": "zoom_in_detail",
                    "keep_elements": [{"label": "element A", "kind": "environment"}],
                }
            ],
        }
    ]
    shot_map = {5: [{"shot_index": 7, "shot_description": "follow-up close-up"}]}
    out = _compute_forward_zoom_targets(deps, shot_map, scene_index=5, shot_index=3)
    assert len(out) == 1
    assert out[0]["scene_index"] == 5
    assert out[0]["shot_index"] == 7
    assert out[0]["description"] == "follow-up close-up"
    assert out[0]["keep_elements"] == [{"label": "element A", "kind": "environment"}]


def test_zoom_target_no_match_when_ref_usage_differs():
    """ref_usage 가 zoom_in_detail 이 아니면 매칭 안 됨."""
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,
            "location_refs": [
                {
                    "scene_index": 5,
                    "shot_index": 3,
                    "ref_usage": "exact_background",  # zoom 아님
                }
            ],
        }
    ]
    out = _compute_forward_zoom_targets(deps, {}, scene_index=5, shot_index=3)
    assert out == []


def test_zoom_target_no_match_when_indices_differ():
    """ref scene/shot 이 query 와 다르면 매칭 안 됨."""
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,
            "location_refs": [
                {
                    "scene_index": 5,
                    "shot_index": 4,  # 4 ≠ 3
                    "ref_usage": "zoom_in_detail",
                }
            ],
        }
    ]
    out = _compute_forward_zoom_targets(deps, {}, scene_index=5, shot_index=3)
    assert out == []


def test_zoom_target_handles_none_dependencies():
    """dependencies 가 None 이어도 안전 ([])."""
    out = _compute_forward_zoom_targets(None, {}, scene_index=5, shot_index=3)
    assert out == []


def test_zoom_target_handles_empty_dependencies():
    """빈 dependencies 안전 ([])."""
    out = _compute_forward_zoom_targets([], {}, scene_index=5, shot_index=3)
    assert out == []


def test_zoom_target_only_first_location_ref_inspected():
    """schema 상 location_refs maxItems=1 — 첫 항목만 검사 (방어 가드)."""
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,
            "location_refs": [
                {"scene_index": 9, "shot_index": 9, "ref_usage": "exact_background"},
                # 두 번째 항목은 zoom_in_detail 이지만 검사 대상 아님
                {"scene_index": 5, "shot_index": 3, "ref_usage": "zoom_in_detail"},
            ],
        }
    ]
    out = _compute_forward_zoom_targets(deps, {}, scene_index=5, shot_index=3)
    assert out == []


def test_zoom_target_multiple_followups_collected():
    """여러 후속 shot 이 같은 source 를 가리키면 모두 수집."""
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,
            "location_refs": [
                {"scene_index": 5, "shot_index": 3, "ref_usage": "zoom_in_detail"}
            ],
        },
        {
            "scene_index": 5,
            "shot_index": 13,
            "location_refs": [
                {"scene_index": 5, "shot_index": 3, "ref_usage": "zoom_in_detail"}
            ],
        },
    ]
    shot_map = {5: [
        {"shot_index": 7, "shot_description": "follow A"},
        {"shot_index": 13, "shot_description": "follow B"},
    ]}
    out = _compute_forward_zoom_targets(deps, shot_map, scene_index=5, shot_index=3)
    assert len(out) == 2
    descs = {item["description"] for item in out}
    assert descs == {"follow A", "follow B"}


def test_zoom_target_description_full_no_truncation():
    """후속 shot description 전문 보존 — CLAUDE.md truncation 금지 룰."""
    long_desc = "A" * 1000  # 절대 truncation 금지
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,
            "location_refs": [
                {"scene_index": 5, "shot_index": 3, "ref_usage": "zoom_in_detail"}
            ],
        }
    ]
    shot_map = {5: [{"shot_index": 7, "shot_description": long_desc}]}
    out = _compute_forward_zoom_targets(deps, shot_map, scene_index=5, shot_index=3)
    assert len(out) == 1
    assert out[0]["description"] == long_desc
    assert len(out[0]["description"]) == 1000


def test_zoom_target_keep_elements_preserved():
    """keep_elements (list[{label, kind}]) shape preserved through forward.

    Area D-next (2026-05-14) — shot_dependency_t2i v6 producer SOT.
    """
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,
            "location_refs": [
                {
                    "scene_index": 5,
                    "shot_index": 3,
                    "ref_usage": "zoom_in_detail",
                    "keep_elements": [
                        {"label": "preserved A", "kind": "environment"},
                        {"label": "preserved B", "kind": "static_prop"},
                    ],
                }
            ],
        }
    ]
    out = _compute_forward_zoom_targets(deps, {}, scene_index=5, shot_index=3)
    assert len(out) == 1
    assert out[0]["keep_elements"] == [
        {"label": "preserved A", "kind": "environment"},
        {"label": "preserved B", "kind": "static_prop"},
    ]


def test_zoom_target_description_fallback_when_meta_missing():
    """shot meta 없으면 description=빈 문자열, scene/shot index 는 보존."""
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,
            "location_refs": [
                {"scene_index": 5, "shot_index": 3, "ref_usage": "zoom_in_detail"}
            ],
        }
    ]
    out = _compute_forward_zoom_targets(deps, {}, scene_index=5, shot_index=3)
    assert len(out) == 1
    assert out[0]["description"] == ""
    assert out[0]["scene_index"] == 5
    assert out[0]["shot_index"] == 7


def test_zoom_target_no_match_when_query_not_in_any_ref():
    """어떤 dep 도 (si, shot_idx) 를 source 로 안 가지면 빈 리스트."""
    deps = [
        {
            "scene_index": 5,
            "shot_index": 7,
            "location_refs": [
                {"scene_index": 5, "shot_index": 99, "ref_usage": "zoom_in_detail"}
            ],
        }
    ]
    out = _compute_forward_zoom_targets(deps, {}, scene_index=5, shot_index=3)
    assert out == []
