"""Phase 9.1 모델 비교 viewer 생성.

좌측: Gemini Image (nano banana 2.0) — production 결과 (백업본)
우측: gpt-image-2 (moderation=low) — 일회성 비교 실험 결과

각 shot마다 두 PNG가 같은 prompt + ref로 생성됐는지 사이드바이사이드 비교.
gpt-image-2 fail shot은 plate 표시.
"""
from __future__ import annotations

import os
import sys
from collections import defaultdict
from pathlib import Path

PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT_ROOT / "backend"))

from app.core.database import SessionLocal
from app.models.project import ImageAsset, SceneStill
from sqlalchemy import and_


def _resolve_image_in_dir(dir_path: Path, scene_index: int, shot_index: int) -> str:
    """디렉토리에서 매칭 PNG 찾기. 두 가지 path 패턴 지원:
    - DB: UUID.png (Gemini 결과)
    - regen 스크립트: S{si:02d}_Shot{shi:02d}.png
    """
    direct = dir_path / f"S{scene_index:02d}_Shot{shot_index:02d}.png"
    if direct.exists():
        return direct.name
    return ""


def main():
    PID = "c00bbe19-a9b5-463f-acfc-806f2e820258"
    EP = "fe165e3a-19c2-4a0f-9acb-e0c9bab0ee5a"
    EP_DIR = PROJECT_ROOT / "projects" / PID / "episodes" / EP
    GEMINI_DIR = PROJECT_ROOT / "projects" / PID / "images" / EP / "scene_phase91_gemini"
    GPT_DIR = PROJECT_ROOT / "projects" / PID / "images" / EP / "scene_gpt_image_2_phase91"

    # 심볼릭 링크 보장 (8090 server cwd = EP_DIR이라 ../../ 못 올라감)
    images_dir = EP_DIR / "images"
    images_dir.mkdir(exist_ok=True)
    for label, src_dir in [("scene_gemini_phase91", GEMINI_DIR),
                            ("scene_gpt_image_2_phase91", GPT_DIR)]:
        link = images_dir / label
        if not link.exists():
            target = Path(f"../../../images/{EP}") / src_dir.name
            link.symlink_to(target)

    DEFECTS = {(5, 13), (6, 1), (10, 6), (12, 8), (12, 16), (13, 6),
               (15, 2), (20, 5), (23, 4), (24, 2), (27, 2), (28, 4), (29, 7)}

    db = SessionLocal()
    stills = (
        db.query(SceneStill)
        .filter(and_(
            SceneStill.project_id == PID,
            SceneStill.episode_id == EP,
            SceneStill.is_selected == True,
        ))
        .order_by(SceneStill.scene_index, SceneStill.shot_index)
        .all()
    )

    rows = []
    for st in stills:
        si = st.scene_index or 0
        shi = st.shot_index or 0
        # Gemini 결과 — DB의 primary ImageAsset file_path (백업본의 같은 파일명)
        primary = (
            db.query(ImageAsset)
            .filter(and_(
                ImageAsset.project_id == PID,
                ImageAsset.episode_id == EP,
                ImageAsset.still_id == st.id,
                ImageAsset.asset_type == "scene",
                ImageAsset.is_primary == 1,
            ))
            .first()
        )
        gemini_name = ""
        if primary and primary.file_path:
            gemini_name = Path(primary.file_path).name
        gemini_rel = f"images/scene_gemini_phase91/{gemini_name}" if gemini_name else ""
        gpt_name = _resolve_image_in_dir(GPT_DIR, si, shi)
        gpt_rel = f"images/scene_gpt_image_2_phase91/{gpt_name}" if gpt_name else ""

        rows.append({
            "si": si, "shi": shi,
            "desc": st.shot_description or "",
            "summary": st.scene_summary or "",
            "gemini": gemini_rel,
            "gpt": gpt_rel,
            "is_defect": (si, shi) in DEFECTS,
        })
    db.close()

    n_total = len(rows)
    n_gpt = sum(1 for r in rows if r["gpt"])
    n_gpt_missing = n_total - n_gpt

    parts = ['<!doctype html><html><head><meta charset="utf-8">']
    parts.append('<title>Phase 9.1 — Gemini vs gpt-image-2 비교</title>')
    parts.append("""<style>
body{margin:0;background:#111;color:#eee;font-family:sans-serif;padding:24px}
h1{font-size:18px;margin:0 0 8px}
h2{font-size:15px;margin:32px 0 12px;border-bottom:1px solid #333;padding-bottom:6px}
.note{font-size:12px;color:#7c8;margin-bottom:24px;line-height:1.6}
.legend{font-size:11px;margin:8px 0 24px;color:#aaa}
.shot-row{margin-bottom:24px;background:#1c1c1c;padding:14px;border-radius:6px}
.shot-row.defect{border:2px solid #f80}
.label{font-size:13px;font-weight:bold;margin-bottom:8px;color:#fff}
.defect-tag{background:#f80;color:#000;font-weight:bold;font-size:10px;padding:2px 6px;border-radius:3px;margin-left:8px}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start}
.col{}
.col-head{font-size:11px;color:#9bc;margin-bottom:4px;font-weight:bold}
.col img{width:100%;display:block;border:1px solid #333;background:#000}
.col .missing{padding:80px 16px;text-align:center;color:#888;border:1px dashed #444;background:#181818;font-size:12px}
.meta{font-size:11px;color:#888;margin-top:8px;line-height:1.4;max-height:60px;overflow:auto}
.summary{font-size:11px;color:#9bc;margin-left:8px;font-weight:normal}
</style></head><body>""")
    parts.append('<h1>Phase 9.1 — Gemini Image (nano banana 2.0) vs gpt-image-2 (moderation=low) 비교</h1>')
    parts.append(f'<div class="note">동일 t2i_prompt + 동일 ref images로 두 모델에 보냄. Gemini는 production 합성 결과 (백업), gpt-image-2는 일회성 실험. gpt-image-2 누락 {n_gpt_missing}건은 OpenAI safety filter가 prompt 거부 (혈흔/폭력 묘사). 결함 13 shot은 주황 테두리.</div>')
    parts.append(f'<div class="legend">전체: {n_total} shot · gpt-image-2 통과: {n_gpt}건 · 누락(safety reject): {n_gpt_missing}건</div>')

    by_scene = defaultdict(list)
    for r in rows:
        by_scene[r["si"]].append(r)

    for si in sorted(by_scene.keys()):
        scene_rows = by_scene[si]
        summary = (scene_rows[0]["summary"] or "")[:140]
        parts.append(f'<h2>S{si} · {len(scene_rows)} shot{"s" if len(scene_rows)>1 else ""}<span class="summary">{summary}</span></h2>')
        for r in scene_rows:
            cls = 'shot-row defect' if r["is_defect"] else 'shot-row'
            defect_tag = '<span class="defect-tag">결함 fix</span>' if r["is_defect"] else ''
            parts.append(f'<div class="{cls}">')
            parts.append(f'<div class="label">S{r["si"]}_Shot{r["shi"]}{defect_tag}</div>')
            parts.append('<div class="grid2">')
            parts.append('<div class="col"><div class="col-head">Gemini Image (nano banana 2.0)</div>')
            if r["gemini"]:
                parts.append(f'<img src="{r["gemini"]}" loading="lazy">')
            else:
                parts.append('<div class="missing">Gemini 결과 없음</div>')
            parts.append('</div>')
            parts.append('<div class="col"><div class="col-head">gpt-image-2 (moderation=low)</div>')
            if r["gpt"]:
                parts.append(f'<img src="{r["gpt"]}" loading="lazy">')
            else:
                parts.append('<div class="missing">safety filter 거부 또는 미생성</div>')
            parts.append('</div>')
            parts.append('</div>')  # grid2
            parts.append(f'<div class="meta">{r["desc"][:300]}</div>')
            parts.append('</div>')  # shot-row

    parts.append('</body></html>')
    out = EP_DIR / "phase91_compare_index.html"
    out.write_text("\n".join(parts), encoding="utf-8")
    print(f"wrote {out} ({out.stat().st_size} bytes)")
    print(f"shots: {n_total}, gemini: {n_total}, gpt-image-2: {n_gpt} (missing {n_gpt_missing})")


if __name__ == "__main__":
    main()
