"""GROUNDING-V2 §2-4b — 조사가 **실제 참조 SOT 까지** 닿는가.

★★★정책 manifest 에서 `reference_required` 로 올려도 여기까지 안 오면
**아무 일도 안 일어난다** (Codex). 실제 보호·생성은
`scene_detail.asset_requirements.required_refs` 만 본다.

★앞선 시험은 `compute_episode_reference_policy` 에서 끝나 이 단절을 못 잡았다
— **조립 자리에서 잰 것**이다.
"""
import pytest

from app.core.episode_reference_policy import compute_episode_reference_policy
from app.core.steps.render_prompt_card import (_research_forced_from_policy,
                                               research_forced_refs)


def _manifest(forced):
    """★스텝이 만드는 모양 그대로 — `grounding` 칸이 **정본**이다."""
    m = compute_episode_reference_policy(
        visible_shot_count={"C01": 1, "P01": 1},
        entity_types={"C01": "character", "P01": "prop"},
        variant_pole_short_ids=set(),
        research_required_short_ids=set(forced))
    m["grounding"] = {"research_required_short_ids": sorted(forced),
                      "override_used": False}
    return m


class TestThePolicyReachesTheCard:
    def test_a_forced_character_becomes_a_character_ref(self):
        forced = _research_forced_from_policy(_manifest({"C01"}))
        assert forced == {"C01"}
        refs = research_forced_refs(forced, [], visible_entities=[
            {"short_id": "C01"}])
        assert refs == [{"kind": "character", "id": "C01",
                         "policy": "required",
                         "reason": "grounding: 조사가 필요하다고 확정됐다"}]

    def test_a_forced_prop_becomes_a_prop_ref(self):
        """★prop 과 character 는 **다른 kind** 다 — 한쪽만 닫으면 반쪽이다."""
        forced = _research_forced_from_policy(_manifest({"P01"}))
        refs = research_forced_refs(forced, [], visible_entities=[
            {"short_id": "P01"}])
        assert refs[0]["kind"] == "prop" and refs[0]["id"] == "P01"

    def test_a_reference_that_is_already_there_is_not_doubled(self):
        forced = {"C01"}
        refs = research_forced_refs(
            forced, [{"kind": "character", "id": "C01", "policy": "required"}],
            visible_entities=[{"short_id": "C01"}])
        assert refs == []

    def test_an_invisible_entity_is_not_forced(self):
        """★이 컷에 없는 것의 참조를 요구하면 「없는 참조」로 막힌다."""
        refs = research_forced_refs({"C09"}, [],
                                    visible_entities=[{"short_id": "C01"}])
        assert refs == []

    def test_a_shot_with_nothing_visible_forces_nothing(self):
        """★★실측 2026-09-03 (attempt 03:07): 보이는 것이 없는 샷에 강제 소품 여섯이 전부 요구돼 PRO-13 이
        서고 scene_detail 이 partial 로 끝났다. 빈 목록은 「없다」지 「모른다」가 아니다."""
        assert research_forced_refs({"P01", "P02", "C01"}, [], visible_entities=[]) == []

    def test_no_visibility_list_at_all_keeps_the_old_meaning(self):
        """None 은 「모른다」— 옛 호출자를 위해 문을 안 건다."""
        refs = research_forced_refs({"P01"}, [], visible_entities=None)
        assert [r["id"] for r in refs] == ["P01"]

    def test_a_reference_required_for_another_reason_is_not_re_added(self):
        """★등장 횟수·variant 로 올라간 것은 **원래부터** 참조가 있다."""
        m = compute_episode_reference_policy(
            visible_shot_count={"C02": 5}, entity_types={"C02": "character"},
            variant_pole_short_ids=set())
        m["grounding"] = {"research_required_short_ids": [],
                          "override_used": False}
        assert m["policy"]["C02"]["mode"] == "reference_required"
        assert _research_forced_from_policy(m) == set()

    def test_the_meaning_is_not_recovered_from_a_reason_string(self):
        """★★★글자로 뜻을 판단하면 **문구만 바꿔도 강제가 사라진다**.

        저장소 계약이 그것을 금지한다 — 구조화된 칸을 읽어야 한다.
        """
        m = _manifest({"C01"})
        for p in m["policy"].values():
            p["reason"] = "문구를 통째로 바꿨다"
        assert _research_forced_from_policy(m) == {"C01"}

    def test_a_manifest_without_the_grounding_field_forces_nothing(self):
        """★칸이 없으면 **없는 것**이다 — 지어내지 않는다."""
        m = compute_episode_reference_policy(
            visible_shot_count={"C01": 1}, entity_types={"C01": "character"},
            variant_pole_short_ids=set())
        assert _research_forced_from_policy(m) == set()

    def test_nothing_is_added_when_research_forced_nothing(self):
        """★★legacy 비회귀 — 빈 집합이면 **한 줄도 안 더한다**."""
        assert _research_forced_from_policy(_manifest(set())) == set()
        assert research_forced_refs(set(), []) == []
        assert research_forced_refs(None, []) == []


# ★조립 자리를 **실제로 태우는** 끝점은 `test_card_endpoint_required_refs.py`
#  에 있다 — source string 이나 signature 를 보는 시험은 그 단절을 못 잡는다.
