"""선정 재판정 비교 갤러리 — 심판 4종을 한 카드에 나란히.

각 샷마다 후보 전부를 크게 걸고, 그 아래 심판별 승자·점수·판정문·하드 위반을
붙인다. 어느 심판이 무엇을 보고 무엇을 놓쳤는지 육안으로 대조하기 위한 것이다.
"""
from __future__ import annotations

import html
import json
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
OUT = ROOT / "artifact/20260807_선정재판정_v6"
RESULT = OUT / "reselect_result.json"
PROJ = "e716bafb-24bb-42b7-aea0-fdb383844ee8"
EPI = "d6a9aa85-b75e-400c-980c-4ee7e876a15b"
RECIPE_REL = f"projects/{PROJ}/images/{EPI}/scene/recipe"

# 사용자가 육안으로 지목한 결함 — 카드마다 무엇을 봐야 하는지 적어 둔다.
NOTE = {
    "S104sh7": "B 가 훨씬 좋은데 A 를 골랐다 (2026-08-07 지목)",
    "S62sh4": "지폐 국적 — A 는 달러, B 는 원화",
    "S42sh4": "폰이 손 없이 뜬다",
    "S13sh3": "핸들 림 이중 / 인물이 조수석",
    "S15sh5": "룸미러 반사가 광학적으로 불가능",
    "S18sh5": "좌석이 돌아가 있다",
    "S88sh6": "총구·시선 방향이 상대를 향하지 않는다",
    "S76sh5": "순찰정",
}
JUDGE_ORDER = ["claude-opus", "gpt", "gemini-pro", "qwen"]
JUDGE_LABEL = {"claude-opus": "Opus 5", "gpt": "Sol",
               "gemini-pro": "Gemini Pro", "qwen": "Qwen 3.8 Max"}

CSS = """
:root{--bg:#12141a;--card:#1b1e26;--fg:#e8eaf0;--dim:#9aa0ae;--line:#2c3140;
--win:#3ddc84;--bad:#ff6b6b;--warn:#ffb454}
*{box-sizing:border-box}
body{margin:0;padding:24px;background:var(--bg);color:var(--fg);
font-family:-apple-system,BlinkMacSystemFont,"Apple SD Gothic Neo",sans-serif;
line-height:1.6}
h1{font-size:22px;margin:0 0 6px}
.sub{color:var(--dim);font-size:13px;margin-bottom:22px}
table.top{border-collapse:collapse;margin-bottom:28px;font-size:13px}
table.top th,table.top td{border:1px solid var(--line);padding:6px 11px;
text-align:center}
table.top th{background:#232733;color:var(--dim);font-weight:600}
.flip{color:var(--warn);font-weight:700}
.truth{color:var(--win);font-weight:700}
.card{background:var(--card);border:1px solid var(--line);border-radius:10px;
padding:18px;margin-bottom:26px}
.card h2{font-size:17px;margin:0 0 4px}
.note{color:var(--warn);font-size:13px;margin-bottom:14px}
.cands{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:16px}
.cand{flex:1 1 320px;min-width:280px}
.cand img{width:100%;border-radius:6px;display:block;border:2px solid transparent}
.cand.old img{border-color:var(--dim)}
.cand .lab{font-size:13px;margin-top:6px;color:var(--dim)}
.badge{display:inline-block;font-size:11px;padding:1px 7px;border-radius:9px;
margin-left:5px;background:#2c3140;color:var(--fg)}
.badge.old{background:#3a4050}
.badge.j{background:#1f3a2c;color:var(--win)}
.judges{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
gap:12px}
.judge{background:#161920;border:1px solid var(--line);border-radius:8px;
padding:12px;font-size:12.5px}
.judge h3{margin:0 0 8px;font-size:13.5px}
.judge .w{color:var(--win);font-weight:700}
.judge .fail{color:var(--bad);font-size:11.5px}
.v{margin:5px 0;padding-left:8px;border-left:2px solid var(--line)}
.hv{color:var(--bad);font-size:11.5px;margin-left:8px}
.rd{color:var(--dim);font-size:11.5px;margin:3px 0 3px 8px}
.scroll{overflow-x:auto}
"""


def esc(s) -> str:
    return html.escape(str(s or ""))


def main() -> None:
    data = json.loads(RESULT.read_text("utf-8"))
    res = data["results"]
    judges = [j for j in JUDGE_ORDER if j in data["judges"]]

    rows = []
    for stem in sorted(res, key=lambda s: (len(s), s)):
        s = res[stem]
        old = s.get("old_selected")
        tds = [f"<td><b>{esc(stem)}</b></td>", f"<td>{esc(old)}</td>"]
        for j in judges:
            w = s["winners"].get(j)
            cls = ' class="flip"' if w and w != old else ""
            tds.append(f"<td{cls}>{esc(w or '실패')}</td>")
        pc = (s.get("production_combined") or {}).get("winner")
        cls = ' class="flip"' if pc and pc != old else ""
        tds.append(f"<td{cls}>{esc(pc or '-')}</td>")
        t = s.get("user_truth")
        tds.append(f'<td class="truth">{esc(t or "-")}</td>')
        rows.append("<tr>" + "".join(tds) + "</tr>")

    head = "".join(f"<th>{esc(JUDGE_LABEL.get(j, j))}</th>" for j in judges)
    top = (
        '<div class="scroll"><table class="top"><tr><th>샷</th><th>구 선정</th>'
        + head + "<th>프로덕션 합의<br>(Opus+Sol)</th><th>사용자 육안</th></tr>"
        + "".join(rows) + "</table></div>"
    )

    cards = []
    for stem in sorted(res, key=lambda s: (len(s), s)):
        s = res[stem]
        old = s.get("old_selected")
        labels = s.get("labels") or []
        winners = s.get("winners") or {}

        cands = []
        for lab in labels:
            marks = []
            if lab == old:
                marks.append('<span class="badge old">구 선정</span>')
            for j in judges:
                if winners.get(j) == lab:
                    marks.append(
                        f'<span class="badge j">{esc(JUDGE_LABEL.get(j, j))}</span>')
            if s.get("user_truth") == lab:
                marks.append('<span class="badge j">사용자</span>')
            cls = "cand old" if lab == old else "cand"
            cands.append(
                f'<div class="{cls}">'
                f'<img src="/{RECIPE_REL}/{stem}_{lab.lower()}.png" loading="lazy">'
                f'<div class="lab">{esc(lab)}{"".join(marks)}</div></div>'
            )

        jcards = []
        for j in judges:
            r = s["per_judge"].get(j) or {}
            if "error" in r:
                jcards.append(
                    f'<div class="judge"><h3>{esc(JUDGE_LABEL.get(j, j))}</h3>'
                    f'<div class="fail">실패: {esc(r["error"])[:200]}</div></div>')
                continue
            body = []
            if r.get("all_candidates_fail"):
                body.append('<div class="fail">★ 후보 전부 실패 선언</div>')
            for rd in r.get("readings") or []:
                lab = rd.get("label")
                txt = (rd.get("direction_ko") or rd.get("direction")
                       or rd.get("built_space_ko") or "")
                if txt:
                    body.append(f'<div class="rd">[{esc(lab)}] {esc(txt)[:220]}</div>')
                for hv in rd.get("hard_violations") or []:
                    body.append(f'<div class="hv">[{esc(lab)}] 하드: {esc(hv)[:200]}</div>')
            for v in r.get("verdicts") or []:
                body.append(
                    f'<div class="v"><b>{esc(v.get("label"))}</b> '
                    f'{esc(v.get("score"))}점 — {esc(v.get("verdict_ko"))}</div>')
            jcards.append(
                f'<div class="judge"><h3>{esc(JUDGE_LABEL.get(j, j))} '
                f'→ <span class="w">{esc(r.get("winner"))}</span></h3>'
                + "".join(body) + "</div>")

        note = NOTE.get(stem, "")
        cards.append(
            f'<div class="card"><h2>{esc(stem)}</h2>'
            + (f'<div class="note">사용자 지목: {esc(note)}</div>' if note else "")
            + f'<div class="cands">{"".join(cands)}</div>'
            + f'<div class="judges">{"".join(jcards)}</div></div>')

    doc = (
        '<meta charset="utf-8">\n'
        "<title>선정 재판정 — 심판 4종 비교 (팩 v6)</title>\n"
        f"<style>{CSS}</style>\n"
        "<h1>선정 재판정 — 심판 4종 비교</h1>\n"
        f'<div class="sub">판정 팩 {esc(data.get("pack"))} · 이미지 생성 0, '
        "판정만 다시 돌렸다. 후보·참조·브리프는 원 실행 그대로이므로 "
        "달라지는 것은 <b>누가 고르는가</b>뿐이다.</div>\n"
        + top + "".join(cards)
    )
    (OUT / "index.html").write_text(doc, encoding="utf-8")
    print(f"→ {OUT/'index.html'}")


if __name__ == "__main__":
    main()
