#!/usr/bin/env python3
"""W2 正式人设定妆种子批 — Kai 10-20 定谳「9锁区按 A5×C1(126/121 双验证)做种子」.

任务：以 combo126 (A5粒子长发×B1全息汉服×C1壁画墙, 二轮优+双验证轴) 为身份种子，
产出 W2 人设定妆资产。两臂：
  A臂 = i2i (seed图=T2_126 原图 896x1344) × 3 pose 变体 —— 身份紧贴种子
  B臂 = t2i 复刻 (prompt=126原始 + seed 锚定变体) —— 供对照
输出: week1/W2_lockup/{armA,armB}/ + 定妆台账 manifest/w2_lockup_ledger.jsonl
"""
import json, time, os, urllib.request, hashlib

ROOT = "/home/kai/shared/virtual-idol/week1"
OUT = f"{ROOT}/W2_lockup"
os.makedirs(f"{OUT}/armA", exist_ok=True)
os.makedirs(f"{OUT}/armB", exist_ok=True)
LEDGER = open(f"{ROOT}/manifest/w2_lockup_ledger.jsonl", "a")

SEED_IMG = "/home/kai/shared/virtual-idol/week1/T2_matrix/full300/T2_126_A5_B2_C1.png"
# Kai 定谳 A5×C1 双验证 = 121 (A5/B1/C1) + 126 (A5/B2/C1)。种子图取 126 (二轮优 slot12)。

BASE = ("young Eastern female singer, unconventional non-standard beauty, cold-toned futuristic tech atmosphere, "
        "ethereal sci-fi oriental aesthetic, distinctive facial features, memorable silhouette, cinematic portrait, "
        "Cyber-Chinese Dunhuang style, Dunhuang Mogao cave mural aesthetic, mineral pigment palette of lead-red, "
        "ochre and azurite with gold-leaf accents, jade-cyan bioluminescence, neon circuit motifs, "
        "semi-realistic illustration, realistic skin shading and fine fabric weave texture, dramatic rim light")
CHAR = ("waist-length straight hair dissolving into data particles")
COST_B1 = "reimagined hanfu with holographic translucent layers"
COST_B2 = "white ceremonial robe with circuit embroidery"
SCEN_C1 = ("standing before a vast weathered Mogao cave wall of faded mineral-pigment flying apsaras, "
           "re-illuminated by holographic light")
TAIL = "soft volumetric lighting, 85mm portrait lens, shallow depth of field"

IDENTITY_LOCK = ("Same character as the reference image: same face, same waist-length hair dissolving into "
                 "data particles, same body proportions, same white ceremonial robe with circuit embroidery. "
                 "The reference image provides the character identity and costume; do not copy its exact pose "
                 "or camera framing.")

POSES = [
    ("p1_three_quarter", "full-body three-quarter view (45 degrees), standing relaxed, looking slightly toward camera"),
    ("p2_front_full", "full-body front view, standing straight, facing camera directly, complete from head to feet"),
    ("p3_side_graceful", "full-body side profile view, one hand gently raised, hair particles drifting"),
]

def led(rec):
    rec["ts"] = time.time()
    LEDGER.write(json.dumps(rec, ensure_ascii=False) + "\n"); LEDGER.flush()

def qwen21_t2i(item_id, prompt, seed, w=896, h=1344):
    out = f"{OUT}/armB/{item_id}.png"
    body = json.dumps({"prompt": prompt, "seed": seed, "width": w, "height": h}).encode()
    req = urllib.request.Request("http://127.0.0.1:10588/api/production/qwen21/generate",
        data=body, headers={"Content-Type": "application/json"})
    t0 = time.time()
    with urllib.request.urlopen(req, timeout=600) as resp:
        j = json.load(resp)
    img = j["data"]["images"][0]
    src = (img.get("url") or "").replace("localhost", "127.0.0.1")
    if not src:
        src = f"http://127.0.0.1:8188/view?filename={img['filename']}&subfolder=&type=output"
    urllib.request.urlretrieve(src, out)
    led({"event": "gen", "phase": "W2-lockup-armB", "asset_id": item_id, "engine": "qwen21",
         "mode": "t2i", "seed": seed, "final_prompt": prompt, "output_path": out,
         "elapsed": round(time.time()-t0, 1), "status": "ok"})
    return out

def qwen21_edit(item_id, prompt, ref_path):
    out = f"{OUT}/armA/{item_id}.png"
    boundary = f"----w2{int(time.time())}"
    with open(ref_path, "rb") as f:
        img_bytes = f.read()
    parts = []
    def field(name, val):
        parts.append(f'--{boundary}\r\nContent-Disposition: form-data; name="{name}"\r\n\r\n{val}\r\n'.encode())
    field("prompt", prompt)
    parts.append(f'--{boundary}\r\nContent-Disposition: form-data; name="image"; filename="seed.png"\r\nContent-Type: image/png\r\n\r\n'.encode())
    parts.append(img_bytes)
    parts.append(f"\r\n--{boundary}--\r\n".encode())
    body = b"".join(parts)
    req = urllib.request.Request("http://127.0.0.1:10588/api/production/qwen21/edit", data=body,
        headers={"Content-Type": f"multipart/form-data; boundary={boundary}"})
    t0 = time.time()
    with urllib.request.urlopen(req, timeout=600) as resp:
        j = json.load(resp)
    img = j["data"]["images"][0]
    src = (img.get("url") or "").replace("localhost", "127.0.0.1")
    if not src:
        src = f"http://127.0.0.1:8188/view?filename={img['filename']}&subfolder=&type=output"
    urllib.request.urlretrieve(src, out)
    led({"event": "gen", "phase": "W2-lockup-armA", "asset_id": item_id, "engine": "qwen21",
         "mode": "i2i", "seed": int(hashlib.md5(item_id.encode()).hexdigest(), 16) % 2**31,
         "final_prompt": prompt, "ref_inputs": [ref_path], "output_path": out,
         "elapsed": round(time.time()-t0, 1), "status": "ok"})
    return out

jobs = []
# A臂: 126 种子图 × 3 pose (i2i, <image1> 绑定句)
for pid, pose in POSES:
    prompt = (f"Repose the singer in <image1>: {pose}. {IDENTITY_LOCK} "
              f"Keep the Mogao cave mural wall background with holographic light. {TAIL}. No text.")
    jobs.append(("edit", f"w2_char_126_{pid}", prompt, SEED_IMG))
# B臂: t2i 对照 — B1 汉服双验证组合 (121/125 轴) 复刻 1 张 + A5×C1 主轴 1 张
p_121 = f"{BASE}, {CHAR}, {COST_B1}, {SCEN_C1}, {TAIL}"
jobs.append(("t2i", "w2_ref_121_B1C1_repro", p_121, 121))
p_126 = f"{BASE}, {CHAR}, {COST_B2}, {SCEN_C1}, {TAIL}"
jobs.append(("t2i", "w2_ref_126_B2C1_repro", p_126, 126))

print(f"SEED={SEED_IMG}")
print(f"jobs={len(jobs)}")
for kind, iid, prompt, arg in jobs:
    print(f"[{time.strftime('%H:%M:%S')}] RUN {iid} ({kind})")
    try:
        out = qwen21_edit(iid, prompt, arg) if kind == "edit" else qwen21_t2i(iid, prompt, arg)
        print(f"  OK {out} {os.path.getsize(out)//1024}KB")
    except Exception as e:
        print(f"  FAIL {iid}: {str(e)[:200]}")
        led({"event": "gen", "phase": "W2-lockup", "asset_id": iid, "status": "fail", "err": str(e)[:200]})
print("W2_LOCKUP_DONE")
