"""Wave5 indoor pose guide 갤러리 빌더 v2 (커밋 금지) — DB 최신 + 실 포즈 캡션."""
import os
import shutil
import subprocess

ROOT = "/Users/manta/Documents/Projects/TheRoad-I1"
EID = "fbf15266-989c-4cc8-8d82-699ea5f628aa"
PID = "7f325c39-7478-4386-a562-27daadd44353"
OUT = os.path.join(ROOT, "scratchpad/wave5_integration/gallery")
BG_DIR = os.path.join(ROOT, "projects", PID, "episodes", EID, "images/background_chain")

# 이번 probe 결과 (실 포즈 반영). group → (QC, target shot, framing, 실 body_pose, bg plate)
GROUPS = {
    "indoor-s14-L04B08": ("QC PASS", "14_5", "close", "수리영: neck craned toward curtain (커튼 쪽으로 목을 뺌)", "L04B08.png"),
    "indoor-s5-L04B06": ("QC PASS", "5_12", "insert", "C08: wrist angled outward (손목을 바깥으로 꺾음, insert)", "L04B06.png"),
    "indoor-s29-L21B01": ("QC PASS", "29_10", "close", "수리영: head on shoulder (기절해 어깨에 기댐) + 인우: shoulder braced under weight (무게 받친 어깨)", "L21B01.png"),
    "indoor-s21-L14B01": ("QC FAIL · text_or_marker_leakage", "21_6", "close", "수리영: hand clamped mouth (손으로 입 막음) — guide에 text 누출 검출→거부", "L14B02.png"),
}


def db(q):
    out = subprocess.run(
        ["psql", "-h", "localhost", "-U", "theroad", "-d", "theroad", "-t", "-A", "-F", "|", "-c", q],
        env={**os.environ, "PGPASSWORD": "theroad_dev_2026"}, capture_output=True, text=True)
    return [l for l in out.stdout.strip().splitlines() if l]


def latest(role):
    rows = db(f"""SELECT DISTINCT ON ((pipeline_metadata_json::jsonb)->>'group_id')
        (pipeline_metadata_json::jsonb)->>'group_id', file_path
        FROM image_asset WHERE episode_id='{EID}' AND pipeline_role='{role}'
        ORDER BY (pipeline_metadata_json::jsonb)->>'group_id', created_at DESC;""")
    return {r.split("|")[0]: r.split("|")[1] for r in rows}


def main():
    shutil.rmtree(OUT, ignore_errors=True)
    os.makedirs(OUT, exist_ok=True)
    guides = latest("indoor_pose_guide")
    unders = latest("indoor_pose_underlay")

    cards = []
    for grp in ["indoor-s14-L04B08", "indoor-s5-L04B06", "indoor-s29-L21B01", "indoor-s21-L14B01"]:
        qc, shot, framing, pose, bgfn = GROUPS[grp]
        bg_src = os.path.join(BG_DIR, bgfn)
        bg_dst = f"{grp}_bg.png"
        shutil.copy(bg_src, os.path.join(OUT, bg_dst))
        und_dst = f"{grp}_underlay.png"
        shutil.copy(os.path.join(ROOT, unders[grp]), os.path.join(OUT, und_dst))
        guide_dst = f"{grp}_guide.png"
        shutil.copy(os.path.join(ROOT, guides[grp]), os.path.join(OUT, guide_dst))
        badge = "#3fb950" if "PASS" in qc else "#f85149"
        cards.append(f"""
        <div class="card">
          <div class="head"><b>{grp}</b> · target {shot} · {framing}
            <span class="badge" style="background:{badge}">{qc}</span></div>
          <div class="pose">실 staging body_pose → guide: <b>{pose}</b></div>
          <div class="row">
            <div class="col"><div class="lbl">① bg plate (배경 SOT)</div><img src="{bg_dst}"></div>
            <div class="col"><div class="lbl">② underlay (faint 등록면)</div><img src="{und_dst}"></div>
            <div class="col"><div class="lbl">③ pose guide (마네킹 — 실 포즈 반영)</div><img src="{guide_dst}"></div>
          </div>
        </div>""")

    html = f"""<!doctype html><html><head><meta charset="utf-8">
<title>Wave5 실내 pose 가이드 v2 — target_id 매칭(실 body_pose)</title>
<style>
body{{background:#0d1117;color:#c9d1d9;font-family:-apple-system,sans-serif;margin:0;padding:24px}}
h1{{font-size:18px}} .sub{{color:#8b949e;font-size:13px;margin-bottom:20px;line-height:1.7}}
.card{{background:#161b22;border:1px solid #30363d;border-radius:10px;padding:16px;margin-bottom:20px}}
.head{{font-size:14px;margin-bottom:6px}} .pose{{font-size:12px;color:#a5d6ff;margin-bottom:12px}}
.badge{{color:#fff;padding:2px 10px;border-radius:10px;font-size:12px;margin-left:10px}}
.row{{display:flex;gap:14px;flex-wrap:wrap}}
.col{{flex:1;min-width:280px}} .col img{{width:100%;border:1px solid #30363d;border-radius:6px;background:#000}}
.lbl{{font-size:12px;color:#8b949e;margin-bottom:6px}}
</style></head><body>
<h1>Wave5 실내 pose 가이드 v2 — target_id→name 매칭으로 실 body_pose 반영 (7f325c39 probe)</h1>
<div class="sub">
★개선: 이전엔 FSC label(영문)↔character_angle(한글) 불일치로 body_pose 누락→마네킹 포즈 generic. 이번엔 <b>target_id(C##)→entity name 매칭</b>으로 실 staging 자세(입막음/기댐/목뺌/손목꺾음)가 guide 프롬프트에 흐름(이름/ID 미주입, 자세 묘사만).<br>
게이트 구조키: candidate 9 → outdoor 3 제외 → indoor 6 judge. judge(single_shot lane): <b>3 admit+QC PASS</b>(s5/s14/s29), 2 judge_no_need, 1 QC FAIL(s21 text 누출→fail-closed).<br>
흐름 ①실 bg plate → ②faint underlay → ③실 포즈 마네킹 등록(흰배경 경로 없음). lineage: 전 guide·underlay input_image_ids=[bg plate UUID](dangling0). DB 영속화 완료.
</div>
{''.join(cards)}
</body></html>"""
    with open(os.path.join(OUT, "index.html"), "w") as f:
        f.write(html)
    print("gallery v2 built, cards:", len(cards))


if __name__ == "__main__":
    main()
