#!/usr/bin/env python3
"""s38 플레이트 s29 풀 체인 — 8개 실외 플레이트 전부.

플레이트별: ①nb2 3롤(마스터 참조) → ②GPT/Gemini 0-10 합산 판정(축:
프롬프트 충실+물리 정합+★마스터 구조 일치+★지역 실물 사실성, 동점=
Gemini 우선) → ③승자 이중 critique → 결함 취합 i2i 수정(0건=승자 그대로)
= bg3_<tag>.png. 판정 기록=plans/s38_light_v1.json plate_chain.
"""
import shutil
import sys
from pathlib import Path

HERE = Path(__file__).parent
sys.path.insert(0, str(HERE))
import forest_lib as F  # noqa: E402
import s37_rooftop_rebuild as W  # noqa: E402
import s38_lightconti_full as L  # noqa: E402
import s29_top_v2 as T  # noqa: E402

import argparse

_ap = argparse.ArgumentParser()
_ap.add_argument("--master", default=str(
    HERE / "out" / "forest_map" / "top2" / "top2_photo_fixed_nb2.png"))
_ap.add_argument("--prefix", default="bg3")
_ARGS = _ap.parse_args() if __name__ == "__main__" else \
    _ap.parse_args([])
MASTER = Path(_ARGS.master)
PREFIX = _ARGS.prefix
LABELS = ["A", "B", "C"]

JUDGE_SYS = "\n".join([
    "You are given ONE image-generation prompt, ONE MASTER location",
    "photograph (the ground truth of this property), and THREE candidate",
    "photographs labelled A, B, C in the order attached, all generated",
    "from that prompt with the master attached as reference.",
    "Pick the ONE candidate that best satisfies ALL of:",
    "- fidelity to the prompt (structure, camera, materials, state,",
    "  exclusions such as no people and no readable text),",
    "- physical coherence (no floating/split volumes, stairs reach",
    "  their destination, possible geometry),",
    "- MASTER consistency (storey count, massing, the external stair's",
    "  full run, rooftop structure placement, walls, gate and yard must",
    "  match the master photograph),",
    "- regional authenticity (the prompt declares a real region and",
    "  era; everything must look like that region's real, ordinary",
    "  built environment — nothing foreign or genericized).",
    "Check every candidate point by point; base verdicts only on what",
    "is visible. Also give every candidate an integer score 0-10.",
    "Output: winner, ranking best-to-worst, and per candidate a score",
    "plus a one-line Korean verdict citing the decisive points.",
])

CRITIQUE_SYS = "\n".join([
    "You are given: an image-generation prompt, a MASTER location",
    "photograph (ground truth), and ONE photograph generated from the",
    "prompt with the master as reference. List what is WRONG with it:",
    "- fidelity to the prompt, - physical coherence,",
    "- MASTER consistency (storeys, massing, stair run, rooftop, walls,",
    "  gate, yard), - regional authenticity per the prompt's region.",
    "Base every finding only on what is visible. Ignore taste.",
    "issues: issue_ko (short Korean line) + fix_en (ONE imperative",
    "English edit fixing exactly that while changing nothing else).",
    "Empty array if nothing is wrong.",
])


def chain(tag, bg_prompt):
    plan = F.load_plan(L.PLAN)
    rec = plan.setdefault("plate_chain", {}).setdefault(tag, {})
    cands = []
    for lab in LABELS:
        out = L.OUT / f"{PREFIX}_{tag}_{lab.lower()}.png"
        F.img_nb2(f"s38_{PREFIX}_{tag}_{lab.lower()}", bg_prompt,
                  [(W.MASTER_REF_LABEL, MASTER)], aspect_ratio="16:9",
                  out_path=out)
        cands.append(out)
    parts = [{"type": "text", "text": "THE PROMPT:\n" + bg_prompt},
             {"type": "text", "text": "MASTER photograph (ground truth):"},
             F.png_data_url(MASTER)]
    for lab, p in zip(LABELS, cands):
        parts.append({"type": "text", "text": f"Candidate {lab}:"})
        parts.append(F.png_data_url(p))
    judge = {}
    for model, mtag in (("gpt", f"s38_{PREFIX}_judge_gpt_{tag}"),
                        ("gemini-pro", f"s38_{PREFIX}_judge_gem_{tag}")):
        judge[model] = F.llm(mtag, JUDGE_SYS, parts, T.JUDGE_SCHEMA,
                             model=model)
    totals = {lab: sum(
        next(v["score"] for v in judge[m]["verdicts"] if v["label"] == lab)
        for m in ("gpt", "gemini-pro")) for lab in LABELS}
    best = max(totals.values())
    tied = [lab for lab, t in totals.items() if t == best]
    gem_rank = judge["gemini-pro"]["ranking"]
    sel = min(tied, key=lambda lab: gem_rank.index(lab)
              if lab in gem_rank else 99)
    sel_path = L.OUT / f"{PREFIX}_{tag}_{sel.lower()}.png"
    rec.update({"totals": totals, "selected": sel, "prefix": PREFIX,
                "verdicts": {m: judge[m]["verdicts"]
                             for m in judge}})
    print(f"[{tag}] judge totals={totals} sel={sel}")
    # critique + fix
    parts2 = [{"type": "text", "text": "THE PROMPT:\n" + bg_prompt},
              {"type": "text", "text": "MASTER photograph:"},
              F.png_data_url(MASTER),
              {"type": "text", "text": "Photograph to examine:"},
              F.png_data_url(sel_path)]
    critique = {}
    for model, mtag in (("gpt", f"s38_{PREFIX}_crit_gpt_{tag}"),
                        ("gemini-pro", f"s38_{PREFIX}_crit_gem_{tag}")):
        critique[model] = F.llm(mtag, CRITIQUE_SYS, parts2,
                                T.CRITIQUE_SCHEMA, model=model)
    fix_lines = [f"- ({t2}) {i['fix_en']}"
                 for model, t2 in (("gpt", "GPT VLM"),
                                   ("gemini-pro", "Gemini VLM"))
                 for i in critique[model]["issues"]]
    rec["critique"] = {m: critique[m]["issues"] for m in critique}
    final = L.OUT / f"{PREFIX}_{tag}.png"
    if final.exists():
        final.unlink()
    if not fix_lines:
        shutil.copy(sel_path, final)
        rec["fix_skipped"] = True
        print(f"[{tag}] 결함 0 — 승자 그대로")
    else:
        fp = "\n\n".join([
            T.FIX_HEAD, "ISSUES TO FIX:\n" + "\n".join(fix_lines),
            "The attached MASTER photograph is the ground truth for the"
            " property's structure — align every fixed feature to it.",
            T.PRESERVE_TAIL])
        F.img_nb2(f"s38_{PREFIX}_fix_{tag}", fp,
                  [(T.FIX_LABEL, sel_path),
                   ("MASTER photograph — ground truth; align the edited"
                    " photo's building to it.", MASTER)],
                  aspect_ratio="16:9", out_path=final)
        rec["fix_skipped"] = False
        print(f"[{tag}] 결함 {len(fix_lines)}건 수정 완료")
    F.save_plan(L.PLAN, plan)


if __name__ == "__main__":
    p37 = F.load_plan(W.PLAN)
    for tag in p37["ext_tags"]:
        chain(tag, p37["ext_bg"][tag]["bg_prompt_en"])
    print("DONE")
