#!/usr/bin/env python3
"""Final scene-still gallery grouped by scene + attached ref roles. 커밋금지."""
import json, shutil
import psycopg2
from pathlib import Path

PID="b0ad5c18-5140-4022-a2b0-805de0e5a385"
EID="0d30302a-51c8-4ac9-98e2-6cd9180512bf"
ROOT=Path("/Users/manta/Documents/Projects/TheRoad-I1")
OUT=ROOT/"scratchpad/e2e_final_gallery"; OUT.mkdir(parents=True, exist_ok=True)
IMGDIR=OUT/"img"; IMGDIR.mkdir(exist_ok=True)

c=psycopg2.connect(host='localhost',user='theroad',password='theroad_dev_2026',dbname='theroad'); cur=c.cursor()
cur.execute("""
  SELECT ia.id, ia.file_path, ia.still_id, ss.scene_index, ss.shot_description, ia.pipeline_metadata_json
  FROM image_asset ia LEFT JOIN scene_still ss ON ss.id = ia.still_id
  WHERE ia.episode_id=%s AND ia.asset_type='scene'
  ORDER BY (ss.scene_index)::int NULLS LAST, ia.created_at
""", (EID,))
rows=cur.fetchall()

from collections import defaultdict
by_scene=defaultdict(list)
role_flags_all=defaultdict(int)
for aid,fp,sid,scn,desc,meta in rows:
    m=json.loads(meta or '{}')
    refs=m.get('actual_attached_refs') or []
    roles=[r.get('role') for r in refs]
    for r in roles: role_flags_all[r]+=1
    by_scene[scn].append((aid,fp,desc,roles))

def copy(fp, name):
    src=ROOT/fp
    if not src.exists(): src=ROOT/"projects"/fp
    if src.exists(): shutil.copy(src, IMGDIR/name); return "img/"+name
    return None

# highlight roles
HL={'composition_guide':'#fc6','dead_state_passport':'#f66','registered_pose_guide':'#6cf',
    'character_state_variant':'#f9a','immobilized_pose_guide':'#6cf',
    'scene_prev_frame':'#9f6','immobilized_prev_frame':'#f96'}
rows_html=[]
for scn in sorted(by_scene, key=lambda x:(int(x) if x is not None else 999)):
    stills=by_scene[scn]
    cells=""
    for aid,fp,desc,roles in stills:
        name=f"s{scn}_{aid[:8]}.png"; img=copy(fp,name)
        rolestr=" ".join(f'<span style="color:{HL.get(r,"#8a8")}">{r}</span>' for r in roles) or '<span style="color:#666">no-refs</span>'
        d=(desc or '')[:110]
        if img: cells+=f'<div class=cell><img src="{img}"><div class=rl>{rolestr}</div><div class=dsc>{d}</div></div>'
    rows_html.append(f'<div class=scn><h3>Scene {scn} <span class=n>({len(stills)} stills)</span></h3><div class=grid>{cells}</div></div>')

html=f"""<html><head><meta charset=utf-8><title>Final E2E stills</title>
<style>body{{background:#0d0d0d;color:#ddd;font-family:sans-serif;margin:10px}}
h3{{margin:16px 0 4px;border-top:1px solid #333;padding-top:8px}} .n{{color:#888;font-size:13px}}
.grid{{display:flex;flex-wrap:wrap;gap:8px}} .cell{{width:340px}}
.cell img{{width:100%;border:1px solid #333;border-radius:4px}}
.rl{{font-size:10px;margin-top:2px}} .dsc{{font-size:10px;color:#999;margin-top:1px}}</style></head><body>
<h1>금월도 E2E 최종 스틸 ({len(rows)} stills, {len(by_scene)} scenes)</h1>
<p style="color:#8a8;font-size:12px">노랑=composition_guide(야외포즈/실내마네킹) · 빨강=dead passport · 파랑=registered pose guide</p>
<p style="color:#8a8;font-size:12px">전체 attached role tally: {dict(role_flags_all)}</p>
{''.join(rows_html)}</body></html>"""
(OUT/"index.html").write_text(html)
print(f"final gallery -> {OUT}/index.html ({len(rows)} stills, {len(by_scene)} scenes)")
print("role tally:", dict(role_flags_all))
# print outdoor + corpse candidate stills
print("\n=== stills with composition_guide (outdoor pose / indoor mannequin) ===")
for scn in sorted(by_scene, key=lambda x:(int(x) if x is not None else 999)):
    for aid,fp,desc,roles in by_scene[scn]:
        if any('guide' in (r or '') or 'dead' in (r or '') or 'pose' in (r or '') for r in roles):
            print(f"  S{scn} {aid[:8]} roles={roles} :: {(desc or '')[:70]}")
