#!/usr/bin/env python3
"""최소 검증판 갤러리 — 샷 6장 + 참조·배경·도면."""
import html
import json
import os
from pathlib import Path

import psycopg2

ROOT = Path("/Users/manta/Documents/Projects/TheRoad-I1")
OUT = ROOT / "artifact" / "20260824_minimal_e2e_scenario" / "index.html"
ST = json.loads((ROOT / "scratchpad" / "minimal_e2e_state.json").read_text())
EID, PID = ST["episode_id"], ST["project_id"]

conn = psycopg2.connect(host="localhost", user="theroad", password="theroad_dev_2026",
                        dbname="theroad")
cur = conn.cursor()

cur.execute("""
    SELECT ia.asset_type, ia.file_path, ss.scene_index, ss.shot_index,
           ec.name, ec.entity_type
    FROM image_asset ia
    LEFT JOIN scene_still ss ON ss.id = ia.still_id
    LEFT JOIN entity_canon ec ON ec.id = ia.entity_id
    WHERE ia.episode_id = %s
    ORDER BY ia.asset_type, ss.scene_index NULLS LAST, ss.shot_index NULLS LAST,
             ec.entity_type NULLS LAST, ec.name NULLS LAST
""", (EID,))
rows = cur.fetchall()

cur.execute("""SELECT step_id, status, applicable_count, completed_count, failed_count
               FROM step_run WHERE episode_id=%s ORDER BY created_at""", (EID,))
steps = cur.fetchall()
conn.close()

scenario = (ROOT / "artifact" / "20260824_minimal_e2e_scenario" / "골목끝.txt").read_text()

groups: dict[str, list] = {}
for t, fp, sc, sh, nm, et in rows:
    groups.setdefault(t, []).append((fp, sc, sh, nm, et))

TITLES = {
    "scene": "샷 이미지 — 최종 결과",
    "reference": "인물·소품 참조",
    "generated": "엔티티 T2I",
    "chain_bg": "배경 (실내 체인)",
    "bgfirst_bg": "배경 (샷별)",
    "place_canon": "실외 장소 캐논",
    "conti_light": "콘티",
    "lane_marker_map": "차선 마커 맵",
    "lane_storyboard_sketch": "차선 스토리보드",
    "floor_plan": "실내 도면",
}
ORDER = ["scene", "conti_light", "floor_plan", "chain_bg", "bgfirst_bg",
         "place_canon", "lane_marker_map", "lane_storyboard_sketch",
         "reference", "generated"]


def rel(fp: str) -> str:
    return "../../" + fp.lstrip("/")


def label(sc, sh, nm, et):
    if sc is not None:
        return f"씬 {sc} · 샷 {sh}"
    if nm:
        return f"{nm} <span class=et>{et or ''}</span>"
    return ""


parts = []
for t in ORDER + [k for k in groups if k not in ORDER]:
    items = groups.get(t)
    if not items:
        continue
    parts.append(f'<h2>{html.escape(TITLES.get(t, t))} '
                 f'<span class=n>{len(items)}장</span></h2><div class=grid>')
    for fp, sc, sh, nm, et in items:
        parts.append(
            f'<figure><a href="{html.escape(rel(fp))}" target=_blank>'
            f'<img loading=lazy src="{html.escape(rel(fp))}"></a>'
            f'<figcaption>{label(sc, sh, nm, et)}</figcaption></figure>')
    parts.append("</div>")

ok = sum(1 for s in steps if s[1] == "completed")
na = sum(1 for s in steps if s[1] == "not_applicable")
zero = [s[0] for s in steps if (s[2] or 0) == 0]

step_rows = "".join(
    f"<tr class='{'zero' if (a or 0)==0 else ''}'><td>{html.escape(sid)}</td>"
    f"<td>{st}</td><td>{c}/{a or 0}</td><td>{f}</td></tr>"
    for sid, st, a, c, f in steps)

OUT.write_text(f"""<meta charset="utf-8">
<title>골목 끝 — 최소 검증판</title>
<style>
:root{{color-scheme:dark}}
body{{background:#111;color:#ddd;font:15px/1.6 -apple-system,'Apple SD Gothic Neo',sans-serif;
     margin:0;padding:28px 32px;max-width:1500px}}
h1{{font-size:24px;margin:0 0 4px}} h2{{font-size:17px;margin:36px 0 12px;
   border-bottom:1px solid #333;padding-bottom:6px}}
.n{{color:#888;font-weight:400;font-size:13px}}
.sum{{color:#9ad;margin-bottom:24px}}
.grid{{display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(260px,1fr))}}
figure{{margin:0}} img{{width:100%;border-radius:6px;background:#000;display:block}}
figcaption{{font-size:12px;color:#999;margin-top:5px}}
.et{{color:#666}}
table{{border-collapse:collapse;font-size:12px;margin-top:10px;width:100%;max-width:640px}}
td{{padding:2px 10px;border-bottom:1px solid #262626}}
tr.zero td{{color:#7a6}}
pre{{background:#181818;padding:16px;border-radius:6px;white-space:pre-wrap;
    font:13px/1.7 'Apple SD Gothic Neo',sans-serif;color:#bbb}}
details{{margin-top:10px}} summary{{cursor:pointer;color:#9ad}}
</style>
<h1>골목 끝 — 전 파이프라인 최소 검증판</h1>
<div class=sum>3씬 · 900자 → beat 6 · shot 15 · <b>선별 6샷</b> ·
자산 {len(rows)}장 · 스텝 {ok} 완료 / {na} 해당없음 · <b>실패 0</b> ·
주행 22:06 → 00:53 (167분)</div>

<details><summary>시나리오 전문</summary><pre>{html.escape(scenario)}</pre></details>
<details><summary>스텝 표 ({len(steps)}개 · 초록 = 일감 0)</summary>
<table>{step_rows}</table></details>

{''.join(parts)}
""", encoding="utf-8")
print(f"→ {OUT}  ({len(rows)}장)")
print("일감 0 스텝:", ", ".join(zero))
