"""샷 trace 의 영향 요약 — 전문이 아니라 요약이다."""
from app.modules.llm.opik_trace import build_influence_summary


def _record():
    return {
        "shot_run_uid": "0190-abc",
        "input_fingerprint": "e6ad7c49d0c20615",
        "ref_mode": "그룹 배경+엔티티 (2택1: 무콘티 승)",
        "share_plan": {"ref_plan": "background"},
        "selected": "B",
        "ranking": ["B", "A"],
        "totals": {"A": 9, "B": 14},
        "verdicts": [{"label": "B", "score": 7, "verdict_ko": "가" * 400},
                     {"label": "A", "score": 5, "verdict_ko": "나" * 400}],
        "refs": [
            {"label": "LOCATION PHOTOGRAPH", "path": "/a/b.png",
             "asset_id": "loc-1", "role": "location_plate"},
            {"label": "CHARACTER REFERENCE — 김선영", "path": "<bytes:870689>",
             "asset_id": "char-1", "role": "character_ref"},
        ],
        "critique": {"issues": [{"issue_ko": "비니가 생략됨"}]},
        "fix_skipped": True,
        "fix_skip_reason": "no_critical_issue",
        "prompt": "P" * 5000,
    }


def test_summary_keeps_decision_axes():
    s = build_influence_summary(_record())
    assert s["selected"] == "B"
    assert s["ranking"] == ["B", "A"]
    assert s["totals"] == {"A": 9, "B": 14}
    assert s["input_fingerprint"] == "e6ad7c49d0c20615"
    assert s["ref_mode"].startswith("그룹 배경+엔티티")
    assert s["fix_applied"] is False
    assert s["fix_skip_reason"] == "no_critical_issue"


def test_summary_lists_refs_with_asset_ids():
    s = build_influence_summary(_record())
    assert s["refs"] == [
        {"label": "LOCATION PHOTOGRAPH", "asset_id": "loc-1",
         "role": "location_plate"},
        {"label": "CHARACTER REFERENCE — 김선영", "asset_id": "char-1",
         "role": "character_ref"},
    ]


def test_summary_never_carries_full_prompt():
    """전문은 span 에 이미 있다 — 두 벌로 두면 어느 쪽이 진짜인지 갈린다."""
    s = build_influence_summary(_record())
    assert "prompt" not in s
    blob = repr(s)
    assert "PPPPPPPPPP" not in blob


def test_verdicts_are_scores_only():
    s = build_influence_summary(_record())
    assert s["verdicts"] == [{"label": "B", "score": 7},
                             {"label": "A", "score": 5}]


def test_missing_fields_are_tolerated():
    s = build_influence_summary({})
    assert s["selected"] is None
    assert s["refs"] == []
    assert s["fix_applied"] is False


# ── 수리 승패는 canonical 판정 하나 (Codex BLOCK 2, 2026-08-24) ────────
#
# `fix_rejudge` 가 **있기만 하면** True 로 보면, 재판정에서 수정본이 **진**
# 샷(winner=="A")까지 「수리 적용」이 된다 — 자산 provenance 의 단일 판정
# (fix_stage_won)과 갈린다. 그 갈림을 없애려고 만든 함수가 그것이다.


def test_fix_losing_rejudge_is_not_applied():
    """★수정본이 졌으면 적용이 아니다 — 최종은 원본 롤이다."""
    s = build_influence_summary({
        "fix_prompt": "P",
        "fix_rejudge": {"winner": "A"},        # A=원본 승
    })
    assert s["fix_applied"] is False


def test_fix_winning_rejudge_is_applied():
    s = build_influence_summary({
        "fix_prompt": "P",
        "fix_rejudge": {"winner": "B"},        # B=수정본 승
    })
    assert s["fix_applied"] is True


def test_critique_disabled_is_not_applied():
    """critique 를 아예 안 돌린 샷은 수리 적용이 아니다."""
    s = build_influence_summary({"critique_skipped": True, "selected": "A"})
    assert s["fix_applied"] is False


def test_legacy_fix_without_rejudge_is_applied():
    """재판정 기록이 없는 옛 산출은 수리 프롬프트가 있으면 채택 — canonical 계약."""
    s = build_influence_summary({"fix_prompt": "P"})
    assert s["fix_applied"] is True


def test_summary_matches_canonical_judgement():
    """★두 판정이 갈리지 않는지 직접 대조한다."""
    from app.modules.pipeline.still_recipe import fix_stage_won

    for rec in (
        {"fix_prompt": "P", "fix_rejudge": {"winner": "A"}},
        {"fix_prompt": "P", "fix_rejudge": {"winner": "B"}},
        {"fix_skipped": True, "fix_prompt": "P"},
        {"critique_skipped": True},
        {"regen_prompt": "R"},
        {},
    ):
        assert build_influence_summary(rec)["fix_applied"] == fix_stage_won(rec)
