"""측정 도구의 **세 갈래 표시**가 갈래를 정말 가르는가 (2026-08-27).

도구가 틀리면 수치가 틀리고, 틀린 수치로 계약을 정하게 된다. 이 판에서
이미 한 번 겪었다 — 인용을 안 찍었을 때 v2·v3 가 둘 다 "2/2 저작"으로만
보여, **무엇으로 통과했는지**를 모른 채 같다고 읽었다.

여기서 잠그는 것은 그 다음 자리다. 신원을 `source_quote` 하나로만 보면,
같은 인용을 **신호**(`scene_text_implied`)와 **버림**이 함께 쓸 때 버린
것이 신호로 찍힌다 (2026-08-27 Codex 재리뷰 BLOCK).
"""

import sys
from pathlib import Path

_TOOLS = Path(__file__).resolve().parents[2] / "tools" / "prompt_measure"
if str(_TOOLS) not in sys.path:
    sys.path.insert(0, str(_TOOLS))

from ab_inscription_source import mark_for  # noqa: E402


def _item(text, source, quote):
    return {"text_native": text, "source": source, "source_quote": quote,
            "reason_ko": "r"}


# ── 세 갈래가 각자 제 표시를 받는가 ──────────────────────────────────

def test_a_kept_item_is_marked_readable():
    it = _item("금일 휴업", "scene_text_quoted", "금일 휴업")
    assert mark_for(it, kept=[it], cues=[]) == "✓ "


def test_a_cue_is_marked_as_a_cue():
    raw = _item("안내", "scene_text_implied", "쪽지를 읽는다")
    cue = {**raw, "text_native": ""}   # 저작 쪽이 문안을 비워서 넘긴다
    assert mark_for(raw, kept=[], cues=[cue]) == "~ "


def test_a_dropped_item_is_marked_dropped():
    it = _item("공고", "inferred", "")
    assert mark_for(it, kept=[], cues=[]) == "✗ "


# ── 같은 인용을 신호와 버림이 나눠 가질 때 ───────────────────────────

def test_a_dropped_item_is_not_mistaken_for_a_cue_sharing_its_quote():
    """★인용 하나로만 보면 **버린 것이 신호로 찍힌다.**

    같은 샷에서 모델이 두 항목을 내고 인용을 똑같이 댈 수 있다. 하나는
    「읽을 것이 있다」는 신호이고, 다른 하나는 그 인용으로 **읽을 글자를
    저작**하려다 검증에 걸려 버려진 것이다 — 정반대 갈래인데 인용이 같다.
    """
    quote = "벽에 붙은 것을 읽는다"
    cue_raw = _item("안내", "scene_text_implied", quote)
    cue = {**cue_raw, "text_native": ""}
    dropped = _item("금일 휴업", "scene_text_quoted", quote)

    assert mark_for(cue_raw, kept=[], cues=[cue]) == "~ "
    assert mark_for(dropped, kept=[], cues=[cue]) == "✗ ", (
        "버린 항목이 인용을 신호와 공유한다고 신호로 찍혔다")


def test_a_kept_item_sharing_a_quote_with_a_cue_stays_readable():
    """살아남은 항목이 신호와 인용을 공유해도 표시가 흔들리지 않는다."""
    quote = "벽에 붙은 것을 읽는다"
    kept = _item("금일 휴업", "world_facts", quote)
    cue = {**_item("안내", "scene_text_implied", quote), "text_native": ""}
    assert mark_for(kept, kept=[kept], cues=[cue]) == "✓ "


def test_two_cues_with_the_same_quote_but_different_wording_both_show():
    """신호 갈래는 문안을 안 본다 — 문안이 달라도 갈래는 같다.

    ★이것이 (출처, 인용) 두 칸으로 **충분한** 이유다. 신호의 통과·탈락은
     그 두 칸만으로 정해지므로(`_filter_grounded` 의 신호 갈래는
     `text_native` 를 안 본다), 같은 두 칸을 가진 두 항목이 한쪽은 신호
     한쪽은 버림이 될 수 없다.
    """
    quote = "쪽지를 읽는다"
    a = _item("안내", "scene_text_implied", quote)
    b = _item("공지", "scene_text_implied", quote)
    cues = [{**a, "text_native": ""}, {**b, "text_native": ""}]
    assert mark_for(a, kept=[], cues=cues) == "~ "
    assert mark_for(b, kept=[], cues=cues) == "~ "


# ── 갈래를 가르는 계약이 저작 쪽과 어긋나지 않는가 ───────────────────

def test_the_cue_branch_really_ignores_the_wording():
    """★위 시험이 기대는 전제를 **저작 코드로** 확인한다.

    「신호 갈래는 문안을 안 본다」가 언젠가 바뀌면 (출처, 인용) 두 칸으로는
    부족해진다. 전제가 깨지면 여기서 먼저 빨개진다.
    """
    from app.modules.pipeline.signage_author import _filter_grounded

    quote = "쪽지를 읽는다"
    items = [_item("안내", "scene_text_implied", quote),
             _item("전혀 다른 글자", "scene_text_implied", quote)]
    kept, cues, dropped = _filter_grounded(
        items, shot_text=f"남자가 {quote}.", place_text="",
        world_facts_block="", pack_selector="3")

    assert not kept and not dropped, (
        "같은 (출처, 인용)인데 갈래가 갈렸다 — 두 칸으로는 못 가른다")
    assert len(cues) == 2
    assert all(c["text_native"] == "" for c in cues), (
        "신호에 발명 문안이 남았다")


def test_both_display_sites_go_through_the_one_function():
    """★표시를 고르는 코드가 **한 벌만** 있어야 한다.

    본 표본과 양성 대조에 같은 코드를 두 벌 두었더니 한쪽만 고쳐졌다
    (2026-08-27 Codex 재리뷰 BLOCK). 함수를 태우는 단위 시험은 그때도
    초록이었을 것이다 — 도구가 그 함수를 안 불렀으니까.
    """
    import ast

    src = (_TOOLS / "ab_inscription_source.py").read_text(encoding="utf-8")
    tree = ast.parse(src)
    calls = [n for n in ast.walk(tree)
             if isinstance(n, ast.Call) and isinstance(n.func, ast.Name)
             and n.func.id == "mark_for"]
    assert len(calls) >= 2, (
        f"표시 함수를 부르는 자리가 {len(calls)}곳 — 본 표본과 양성 대조 "
        "둘 다 같은 함수를 타야 한다")
    for c in calls:
        kws = {k.arg for k in c.keywords if k.arg}
        assert {"kept", "cues"} <= kws, (
            f"line {c.lineno}: 갈래 두 목록을 다 안 넘긴다 — {sorted(kws)}")
