"""'사랑했지만' 1화 최종 리포트 — gw1(금월도 제출본) 형식.

씬별 접기 섹션: 씬 헤딩 + 대본 원문(.para) + 샷별[최종 이미지(모달
확대) + Type/Beat 메타 + 샷 설명 캡션 + 과정 보기 링크].

데이터: scene_still DB(is_selected, beat_title·shot_description·
selected_variant) + scene_save CP(씬 원문). 이미지는 love1 배포본의
images/S{si}sh{shi}_sel.jpg 를 그대로 참조 — 추가 변환 없음.

사용: .venv/bin/python build_saranghae_report.py
"""
from __future__ import annotations

import html as H
import json
import re
import subprocess
from collections import defaultdict
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
PROJ = "c7e3b2e7-c545-4516-93b2-62a51a74d794"
EPI = "7c902020-4451-4967-9eb6-1e53c2b9b717"
CP_SAVE = (ROOT / "projects" / PROJ / "checkpoints" / "episodes" / EPI
           / "scene_save" / "manifest.json")
OUT = ROOT / "artifact" / "20260812_사랑했지만_최종215_갤러리" / "report.html"

STYLE = """
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Apple SD Gothic Neo', 'Malgun Gothic', sans-serif; background: #111; color: #e0e0e0; padding: 20px; }
h1 { text-align: center; padding: 30px 0; font-size: 28px; color: #fff; border-bottom: 1px solid #333; margin-bottom: 10px; }
.sub { text-align:center; color:#999; margin-bottom: 30px; font-size: 14px; }
.sub a { color:#8cf }
.scene { margin-bottom: 40px; border: 1px solid #333; border-radius: 8px; overflow: hidden; }
.scene h2 { background: #1a1a2e; padding: 14px 20px; font-size: 18px; color: #a0c4ff; user-select: none; }
.scene h2 .toggle-arrow { font-size: 12px; margin-left: 8px; transition: transform 0.2s; display: inline-block; }
.scene.open h2 .toggle-arrow { transform: rotate(90deg); }
.scene .scene-body { display: none; padding: 16px 20px; }
.scene.open .scene-body { display: block; }
.shot-count { color: #777; font-size: 14px; }
.para { background: #191919; border-left: 3px solid #444; padding: 12px 16px; margin-bottom: 18px; line-height: 1.7; font-size: 14px; color: #ccc; white-space: pre-wrap; }
.inline-shot { margin: 18px 0 26px; }
.img-wrap { position: relative; }
.img-wrap img { width: 100%; max-width: 960px; border-radius: 6px; cursor: zoom-in; display: block; }
.meta { margin-top: 6px; font-size: 12px; color: #8a8; }
.shot-caption { margin-top: 8px; font-size: 14px; color: #ddd; line-height: 1.6; max-width: 960px; }
.proc { font-size: 12px; margin-top: 4px; }
.proc a { color: #68a; text-decoration: none; }
.modal { display:none; position: fixed; inset: 0; background: rgba(0,0,0,.92); z-index: 10; align-items: center; justify-content: center; flex-direction: column; }
.modal.active { display: flex; }
.modal img { max-width: 96vw; max-height: 88vh; }
.modal .m-meta { color:#aaa; font-size: 13px; margin-top: 10px; }
"""

MODAL = """
<div class="modal" id="modal" onclick="closeModal()">
  <img id="modal-img" src="">
  <div class="m-meta" id="modal-meta"></div>
</div>
<script>
function openModal(el) {
  const modal = document.getElementById('modal');
  document.getElementById('modal-img').src = el.src;
  const metaEl = el.parentElement.querySelector('.meta');
  document.getElementById('modal-meta').innerHTML = metaEl ? metaEl.innerHTML : '';
  modal.classList.add('active');
  document.body.style.overflow = 'hidden';
}
function closeModal() {
  document.getElementById('modal').classList.remove('active');
  document.body.style.overflow = '';
}
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); });
</script>
"""


def esc(x) -> str:
    return H.escape(str(x if x is not None else ""))


def q(sql: str) -> list[list[str]]:
    out = subprocess.run(
        ["psql", "-h", "localhost", "-U", "theroad", "-d", "theroad",
         "-t", "-A", "-F", "\x1f", "-c", sql],
        env={"PGPASSWORD": "theroad_dev_2026", "PATH": "/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin"},
        capture_output=True, text=True, check=True)
    return [line.split("\x1f") for line in out.stdout.splitlines() if line]


def main() -> None:
    rows = q(
        "SELECT scene_index, shot_index, coalesce(beat_title,''),"
        " coalesce(shot_description,''), coalesce(selected_variant,'')"
        f" FROM scene_still WHERE episode_id='{EPI}' AND is_selected=true"
        " ORDER BY scene_index, shot_index;")
    shots = defaultdict(list)
    for si, shi, beat, desc, var in rows:
        shots[int(si)].append((int(shi), beat, desc, var))

    segs = {s["scene_index"]: s for s in
            (json.loads(CP_SAVE.read_text("utf-8")).get("data") or {})
            .get("segments", [])}
    # 선정 롤(A/B)은 DB selected_variant 가 비어 있어 recipe records 에서
    recs = json.loads((ROOT / "projects" / PROJ / "images" / EPI / "scene"
                       / "recipe" / "records.json").read_text("utf-8"))
    sel_of = {k: v.get("selected") for k, v in recs.items()
              if "::" not in k and isinstance(v, dict)}

    body = []
    for si in sorted(shots):
        seg = segs.get(si) or {}
        heading = (seg.get("heading") or f"S#{si}.").strip()
        title = re.sub(r"^S#\d+\.\s*", "", heading)
        text = (seg.get("text") or "").strip()
        cnt = len(shots[si])
        cells = []
        for shi, beat, desc, var in shots[si]:
            tag = f"S{si}sh{shi}"
            sel = var or sel_of.get(tag) or ""
            sel_txt = f" · 선정 롤 {esc(sel)}" if sel else ""
            cells.append(
                f"<div class='inline-shot' id='{tag}'>"
                f"<div class='img-wrap'>"
                f"<img loading='lazy' src='images/{tag}_sel.jpg' onclick='openModal(this)'>"
                f"<div class='meta'><span>{tag}{sel_txt}</span><br>"
                f"<span>Beat: {esc(beat)}</span></div></div>"
                f"<div class='shot-caption'>{esc(desc)}</div>"
                f"<div class='proc'><a href='process.html#{tag}'>제작 과정 보기 →</a></div>"
                f"</div>")
        body.append(
            "<section class='scene open'>"
            "<h2 onclick=\"this.parentElement.classList.toggle('open')\""
            " style='cursor:pointer'>"
            f"씬 {si}: {esc(title)} <span class='shot-count'>({cnt}샷)</span>"
            " <span class='toggle-arrow'>&#9654;</span></h2>"
            f"<div class='scene-body'><div class='para'>{esc(text)}</div>"
            f"{''.join(cells)}</div></section>")

    total = sum(len(v) for v in shots.values())
    html = (
        "<!DOCTYPE html>\n<html lang=\"ko\">\n<head>\n"
        "<meta charset=\"UTF-8\">\n"
        "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
        "<title>사랑했지만 1화</title>\n"
        f"<style>{STYLE}</style>\n</head>\n<body>\n"
        "<h1>사랑했지만 1화</h1>"
        f"<div class='sub'>{len(shots)}씬 · 최종 스틸 {total}샷 · 시대 1982-1991"
        " · <a href='process.html'>전체 제작 과정(중간 이미지·판정 기록) 보기</a></div>"
        + "\n".join(body) + MODAL + "\n</body>\n</html>")
    OUT.write_text(html, "utf-8")
    print(f"완료: {len(shots)}씬 {total}샷 → {OUT}")


if __name__ == "__main__":
    main()
