#!/usr/bin/env python3
"""T2 三标签矩阵 300 组合表构建 (S1) — Notion 行动计划 §1/§2/§3 为真源.
输出: matrix_300.csv + prompt_pack.json (prompt/seed 全量可复算)
combo_id = 1..300, 顺序 a→b→c; seed = combo_id (首批探针确定性)
"""
import csv, json, hashlib, re, os

ROOT = "/home/kai/shared/virtual-idol/week1/T2_matrix"
os.makedirs(ROOT, exist_ok=True)

FIXED_CORE = ("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")

TAIL = "soft volumetric lighting, 85mm portrait lens, shallow depth of field"

A_AXIS = [
"A1 sleek black hime-cut hair with glowing blue streaks",
"A2 long silver-white hair floating weightlessly",
"A3 short asymmetric bob with neon underglow",
"A4 traditional Chinese updo with mechanical hairpins",
"A5 waist-length straight hair dissolving into data particles",
"A6 twin buns with fiber-optic strands",
"A7 shaved side with long braided topknot",
"A8 flowing ink-wash hair merging into smoke",
"A9 layered cut with holographic highlights",
"A10 bald with luminous circuit-pattern scalp tattoos",
]
B_AXIS = [
"B1 reimagined hanfu with holographic translucent layers",
"B2 white ceremonial robe with circuit embroidery",
"B3 tech-wear qipao with LED piping",
"B4 flowing ink-painting dress, fabric made of light",
"B5 minimalist futuristic gown with jade ornaments",
"B6 armored opera costume, porcelain-textured plates with gold-leaf accents",
]
C_AXIS = [
"C1 standing before a vast weathered Mogao cave wall of faded mineral-pigment flying apsaras, re-illuminated by holographic light",
"C2 apsara dance pose in a dark cave hall, trailing ribbons dissolving into neon circuit patterns, floating lantern drones",
"C3 seated on a lotus throne of glowing circuit mandala, cave darkness with volumetric haze",
"C4 walking a corridor of cave niches where murals turn into holographic light projections",
"C5 beneath a collapsed cave dome opening to a night sky, constellations rendered as gilded mural patterns",
]

NEGATIVE = ["photorealistic human face", "real person likeness", "western fantasy armor",
"photography studio lighting", "oversaturated cyberpunk purple-pink generic look",
"flat cel shading", "modern city skyline", "text", "watermark"]

def strip_tag(s):
    return re.sub(r'^[ABC]\d+\s+', '', s)

def combo_id(a, b, c):
    return (a * 6 + b) * 5 + c + 1  # a,b,c 0-based → 1..300

def build_prompt(a_s, b_s, c_s):
    return f"{FIXED_CORE}, {strip_tag(a_s)}, {strip_tag(b_s)}, {strip_tag(c_s)}, {TAIL}"

rows = []
for a, a_s in enumerate(A_AXIS):
    for b, b_s in enumerate(B_AXIS):
        for c, c_s in enumerate(C_AXIS):
            cid = combo_id(a, b, c)
            rows.append({
                "combo_id": cid, "seed": cid,
                "A": a_s.split()[0], "B": b_s.split()[0], "C": c_s.split()[0],
                "a_text": strip_tag(a_s), "b_text": strip_tag(b_s), "c_text": strip_tag(c_s),
                "engine": "qwen21", "ratio": "9:16", "width": 896, "height": 1344,
                "prompt_full": build_prompt(a_s, b_s, c_s),
            })

with open(f"{ROOT}/matrix_300.csv", "w", newline="") as f:
    w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
    w.writeheader(); w.writerows(rows)

json.dump({"fixed_core": FIXED_CORE, "tail": TAIL, "negative_words": NEGATIVE,
           "axes": {"A": A_AXIS, "B": B_AXIS, "C": C_AXIS},
           "seed_rule": "seed = combo_id (1..300)",
           "engine": "qwen21 896x1344 via KAP :10588"}, open(f"{ROOT}/prompt_pack.json", "w"),
          ensure_ascii=False, indent=1)

# ---- sanitizer: MJ 参数/禁词扫描 ----
bad = []
for r in rows:
    p = r["prompt_full"]
    for pat in ["--", "stylize", "chaos", "cref", "sref", "midjourney", "mj"]:
        if pat in p.lower(): bad.append((r["combo_id"], pat))
    # fixed core integrity: 每条 prompt 必须以 FIXED_CORE 开头
    if not p.startswith(FIXED_CORE): bad.append((r["combo_id"], "CORE_DRIFT"))
print(f"matrix_300.csv rows={len(rows)} sanitizer_bad={len(bad)}")
if bad: print(bad[:10])

# ---- 首批 20 组合（全轴覆盖探针）----
first = set()
for a in range(10): first.add((a, 0, 0))            # A 扫描 ×10
for b in range(1, 6): first.add((3, b, 0))          # B 扫描 ×5
for c in range(1, 5): first.add((3, 0, c))          # C 扫描 ×4
first.add((4, 3, 2))                                 # wild ×1
first_ids = sorted(combo_id(*t) for t in first)
json.dump(first_ids, open(f"{ROOT}/first20_ids.json", "w"))
print(f"first20 ids = {first_ids}")
assert len(first_ids) == 20
