"""shot_validator 재실행 전후 대조 — 팩 v7 이 실제로 무엇을 바꿨는지.

묻는 것은 둘이다.
  ① **구조 지표** — 몇 %를 고쳤고, characters 를 몇 %에서 늘렸나. 코드가 셀
     수 있는 사실만 센다.
  ② **지목 샷의 실제 문구** — 태도가 자세로 바뀌던 자리(S88sh3)와 공중 순간
     (S1sh7·S2sh5·S93sh8)이 어떻게 달라졌는지 원문 그대로 보여 준다.
     의미 판정은 사람이 한다 — 여기서 어휘로 판단하지 않는다.

사용
  .venv/bin/python diff_validator.py [--stems S88sh3,S2sh5,...]
"""
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path
from typing import Any, Dict, Tuple

ROOT = Path(__file__).resolve().parent.parent
PROJ = "e716bafb-24bb-42b7-aea0-fdb383844ee8"
EPI = "d6a9aa85-b75e-400c-980c-4ee7e876a15b"
CPD = ROOT / f"projects/{PROJ}/checkpoints/episodes/{EPI}"

# 육안 판정이 지목한 자리 — 태도 변형 1건 + 공중 순간 3건.
DEFAULT_STEMS = ["S88sh3", "S1sh7", "S2sh5", "S93sh8"]


def load(path: Path) -> Dict[Tuple[int, int], Dict[str, Any]]:
    d = json.loads(path.read_text("utf-8"))["data"]
    out: Dict[Tuple[int, int], Dict[str, Any]] = {}
    for s in d.get("scenes") or []:
        si = s.get("scene_index")
        for sh in s.get("shots") or []:
            out[(si, sh.get("shot_index"))] = sh
    return out


def tag(k: Tuple[int, int]) -> str:
    return f"S{k[0]}sh{k[1]}"


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--stems", default=",".join(DEFAULT_STEMS))
    args = ap.parse_args()

    new_path = CPD / "shot_validator" / "manifest.json"
    olds = sorted((CPD / "shot_validator").glob("manifest_*.json"))
    if not new_path.exists() or not olds:
        print("대조 불가 — 새 manifest 또는 백업이 없다", file=sys.stderr)
        raise SystemExit(1)
    old_path = olds[-1]
    print(f"이전 = {old_path.name}\n이후 = {new_path.name}\n")

    old, new = load(old_path), load(new_path)
    both = sorted(set(old) & set(new))
    print(f"대조 가능 shot: {len(both)} (이전 {len(old)} / 이후 {len(new)})\n")

    # ── ① 구조 지표 ──────────────────────────────────────────────────
    def stats(m: Dict[Tuple[int, int], Dict[str, Any]], src: Dict) -> Dict:
        rew = sum(1 for k in both if m[k].get("original_description"))
        grew = sum(1 for k in both
                   if len(m[k].get("characters") or [])
                   > len(src[k].get("characters") or []))
        return {"rewrote": rew, "chars_grew": grew}

    # 이전·이후 모두 원문(original_description)이 있으면 그것이 shot_extract
    # 원본이다 — 없으면 그 판이 손대지 않은 것이므로 description 이 원본.
    def origin(m, k):
        sh = m.get(k) or {}
        return sh.get("original_description") or sh.get("description") or ""

    src_old = {k: {"characters": old[k].get("characters")} for k in both}
    n = len(both) or 1
    so, sn = stats(old, src_old), stats(new, src_old)
    print("구조 지표 (이전 → 이후)")
    print(f"  문구를 고친 shot : {so['rewrote']:4d} ({so['rewrote']*100//n}%)"
          f"  →  {sn['rewrote']:4d} ({sn['rewrote']*100//n}%)")

    # description 이 실제로 달라진 shot (같은 원문에서 다른 결과가 나온 것)
    changed = [k for k in both
               if (old[k].get("description") or "")
               != (new[k].get("description") or "")]
    print(f"  두 판의 결과가 다른 shot : {len(changed)} ({len(changed)*100//n}%)")

    # characters 원소 수 변화 — 이전 판 대비
    grew = [k for k in both
            if len(new[k].get("characters") or [])
            > len(old[k].get("characters") or [])]
    shrank = [k for k in both
              if len(new[k].get("characters") or [])
              < len(old[k].get("characters") or [])]
    print(f"  characters 늘어난 shot : {len(grew)} / 줄어든 shot : {len(shrank)}")

    # ── ② 지목 샷 원문 대조 ──────────────────────────────────────────
    want = {s.strip() for s in args.stems.split(",") if s.strip()}
    print("\n" + "=" * 70)
    print("지목 샷 원문 대조 — 의미 판정은 사람이 한다")
    print("=" * 70)
    for k in both:
        if tag(k) not in want:
            continue
        o, w = old[k], new[k]
        print(f"\n■ {tag(k)}")
        print(f"  [원본 ] {origin(o, k)}")
        print(f"  [이전 ] {o.get('description')}")
        print(f"    사유: {o.get('validator_reason') or '(손대지 않음)'}")
        print(f"  [이후 ] {w.get('description')}")
        print(f"    사유: {w.get('validator_reason') or '(손대지 않음)'}")
        print(f"  characters: {o.get('characters')} → {w.get('characters')}")

    # 결과가 달라진 shot 중 무작위 표본이 아니라 **앞에서부터** 몇 개 —
    # 재현 가능해야 다음 회차와 비교된다.
    print("\n" + "=" * 70)
    print("결과가 달라진 shot 앞에서부터 6건 (지목 샷 제외)")
    print("=" * 70)
    shown = 0
    for k in changed:
        if tag(k) in want:
            continue
        print(f"\n■ {tag(k)}")
        print(f"  [이전] {old[k].get('description')}")
        print(f"  [이후] {new[k].get('description')}")
        print(f"    사유: {new[k].get('validator_reason') or '(손대지 않음)'}")
        shown += 1
        if shown >= 6:
            break


if __name__ == "__main__":
    main()
