"""CAMERA 절 A/B 갤러리 — 카메라 지시가 있을 때와 없을 때를 나란히.

`ab_camera_clause.py` 가 만든 두 장을 샷마다 붙이고, 그 사이에
`shot_staging` 이 쓴 `camera_direction` 원문을 놓는다.

★판정은 육안이다. 「달라졌다」와 「지시대로다」는 다른 물음이고,
  낱말로는 카메라가 어디 섰는지 못 가른다.

usage: build_camera_clause_gallery.py
"""
import html
import json
import re
import subprocess
from pathlib import Path

ROOT = Path("/Users/manta/Documents/Projects/TheRoad-I1")
PROJ = "da049582-2c6d-492c-979d-f468d61bab6e"
EPI = "fb7a883f-baac-4145-9131-732ce628d474"
OUT = ROOT / "artifact" / "20260825_camera_clause_ab"
CP = (ROOT / "projects" / PROJ / "checkpoints" / "episodes" / EPI
      / "shot_staging" / "manifest.json")
RE_CAM = re.compile(r"^- CAMERA: (.*)$", re.M)
RE_SCALE = re.compile(r"^- FRAMING SCALE: (.*)$", re.M)


def lan_ip() -> str:
    try:
        return subprocess.run(["ipconfig", "getifaddr", "en0"],
                              capture_output=True, text=True, timeout=5).stdout.strip()
    except Exception:
        return "127.0.0.1"


def main() -> int:
    plan_f = OUT / "_plan.json"
    plan = json.loads(plan_f.read_text(encoding="utf-8")) if plan_f.exists() else []
    removed_by = {p["shot"]: p.get("removed", {}) for p in plan}
    refs_by = {p["shot"]: p.get("labels", []) for p in plan}
    kind_by = {p["shot"]: p.get("kind", "") for p in plan}
    files_by = {p["shot"]: [f.split("/")[-1] for f in p.get("refs", [])] for p in plan}

    # camera_direction 원문 — 걷어낸 그 값
    staging = {}
    if CP.exists():
        for s in json.loads(CP.read_text(encoding="utf-8")).get("data", {}).get("shots") or []:
            staging[f"S{s.get('scene_index')}sh{s.get('shot_index')}"] = {
                "cd": s.get("camera_direction") or "",
                "fs": s.get("framing_scale") or "",
            }

    shots = sorted({p.name.rsplit("_", 1)[0] for p in OUT.glob("S*_[AB].png")})
    rows = []
    for sh in shots:
        a, b = OUT / f"{sh}_A.png", OUT / f"{sh}_B.png"
        st = staging.get(sh, {})
        cd = html.escape(st.get("cd", "")) or "(기록 없음)"
        fs = html.escape(st.get("fs", ""))
        rm = removed_by.get(sh, {})
        rmtxt = " · ".join(f"{k} {v}자" for k, v in rm.items() if v)
        labels = " · ".join(
            html.escape(f"{l.split('—')[0].strip().split('(')[0].strip()}({f})")
            for l, f in zip(refs_by.get(sh, []), files_by.get(sh, [])))
        kind = kind_by.get(sh, "")
        warn = ("<div class=\"warn\">★이 샷은 <b>마네킹 치환형</b>입니다 — 프롬프트가 "
                "「KEEP EXACTLY: … the camera framing」이라 <b>카메라를 첫 이미지가 정합니다</b>. "
                "텍스트 절을 빼도 카메라는 안 움직이는 것이 정상입니다.</div>"
                if kind == "마네킹치환" else "")

        def cell(p: Path, tag: str, title: str, note: str) -> str:
            if not p.exists():
                return (f'<figure class="miss"><div class="ph">아직 없음</div>'
                        f'<figcaption><b>{title}</b></figcaption></figure>')
            return (f'<figure><span class="tag t{tag}">{tag}</span>'
                    f'<img loading="lazy" src="{p.name}" alt="{sh} {tag}">'
                    f'<figcaption><b>{title}</b><br><span class="note">{note}</span>'
                    f'</figcaption></figure>')

        rows.append(f"""
  <section class="shot">
    <h2>{sh} <span class="kind">{html.escape(kind)}</span></h2>
    {warn}
    <div class="dir">
      <div class="dir-h">B 에서 걷어낸 것 — <b>{html.escape(rmtxt)}</b></div>
      <p>{cd}</p>
      {f'<div class="meta">FRAMING SCALE: {fs}</div>' if fs else ''}
      <div class="meta">양쪽에 똑같이 붙인 참조: {labels}</div>
    </div>
    <div class="pair">
      {cell(OUT / f"{sh}_A.png", "A", "카메라 절 있음", "기록된 프롬프트 그대로")}
      {cell(OUT / f"{sh}_B.png", "B", "카메라 절 없음", "CAMERA · FRAMING SCALE · Compose 지시만 제거")}
    </div>
  </section>""")

    doc = f"""<meta charset="utf-8">
<title>CAMERA 절이 없으면 — A/B</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
  :root {{ color-scheme: light dark; }}
  body {{ font: 15px/1.65 -apple-system, "Apple SD Gothic Neo", sans-serif;
         margin: 0; padding: 24px; background: #0f1115; color: #e6e8ee; }}
  h1 {{ font-size: 22px; margin: 0 0 4px; }}
  .lede {{ color: #a8b0c0; max-width: 78ch; margin: 0 0 24px; }}
  .lede b {{ color: #ffd479; }}
  .shot {{ margin: 0 0 40px; padding: 0 0 28px; border-bottom: 1px solid #222732; }}
  h2 {{ font-size: 17px; margin: 0 0 10px; }}
  .dir {{ background: #171a21; border-left: 3px solid #ffd479;
          padding: 12px 16px; border-radius: 0 8px 8px 0; margin: 0 0 14px; }}
  .dir-h {{ font-size: 12px; color: #8f97a8; margin-bottom: 6px; }}
  .dir p {{ margin: 0; }}
  .meta {{ margin-top: 8px; font-size: 12px; color: #8f97a8; }}
  .pair {{ display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }}
  @media (max-width: 800px) {{ .pair {{ grid-template-columns: 1fr; }} }}
  figure {{ margin: 0; position: relative; }}
  figure img {{ width: 100%; border-radius: 6px; display: block; background: #000; }}
  .tag {{ position: absolute; top: 8px; left: 8px; z-index: 1;
          font-size: 12px; font-weight: 700; padding: 3px 9px; border-radius: 5px; }}
  .tA {{ background: #2b6cb0; color: #fff; }}
  .tB {{ background: #b7791f; color: #fff; }}
  figcaption {{ font-size: 12px; color: #c3c9d6; padding: 6px 2px 0; }}
  .note {{ color: #8f97a8; }}
  .miss .ph {{ aspect-ratio: 16/9; display: grid; place-items: center;
               background: #171a21; border-radius: 6px; color: #565e6e; }}
  .kind {{ font-size: 12px; font-weight: 400; color: #8f97a8; margin-left: 8px; }}
  .warn {{ background: #2a1f14; border-left: 3px solid #d69e2e; color: #e8d5b0;
           padding: 10px 14px; border-radius: 0 8px 8px 0; margin: 0 0 12px;
           font-size: 13px; }}
</style>
<h1>CAMERA 절이 없으면 어떻게 되나</h1>
<p class="lede">
  같은 샷·같은 참조·같은 프롬프트인데 <b>B 에서만 카메라 축 셋을 뺐습니다</b> —
  <code>- CAMERA:</code>(shot_staging 이 쓴 문장) · <code>- FRAMING SCALE:</code> ·
  「Compose the frame exactly as specified above」 한 문장.
  <b>KEY BACKGROUND ELEMENTS 는 양쪽 다 남겼습니다</b> — 그것은 카메라 지시가 아니라
  「무엇이 화면에 있나」라서, 같이 빼면 무엇 때문에 달라졌는지 못 가릅니다.
  참조 이미지는 기록된 라벨 그대로 양쪽에 똑같이 붙였습니다.
</p>
{''.join(rows)}
"""
    OUT.mkdir(parents=True, exist_ok=True)
    (OUT / "index.html").write_text(doc, encoding="utf-8")
    have = sum(1 for sh in shots for x in "AB" if (OUT / f"{sh}_{x}.png").exists())
    print(f"샷 {len(shots)} · 이미지 {have}/{len(shots)*2}")
    print(f"→ http://{lan_ip()}:8940/artifact/20260825_camera_clause_ab/")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
