#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""make_h3_prompts.py — 为选定的 H3 动态镜头生成「官方 Director 六段式」提示词。

格式依据项目实测跑通的官方规范（见 10-视频生成流水线/体操服练舞室22s/超人三段v2/seg1_prompt.txt）：
  subject_definitions / summary / retention_analysis / detailed_description /
  overall_soundscape / non_diegetic_music

要点：
  · <Picture 1> = 该镜关键帧（构图与身份锚点）；<Picture 2>… = 参与角色的设定图（身份锚点）
  · 明确要求保持 **平面水墨绘本 2D 风格**（H3 默认会往写实/3D 漂）
  · detailed_description 按镜头时长切 3 个时间码段落
  · non_diegetic_music 一律写 None（成片配原曲，H3 自带音频会在剪辑里丢弃）
输出：prompts/h3/SXX.txt
"""
import json, os, sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent
os.chdir(ROOT)
sys.path.insert(0, str(ROOT))
from applycute import CHARS, MONSTERS, SCENES  # noqa: E402


def desc_of(key):
    """角色/妖怪的造型描述（CHARS 是 (desc, ratio)，MONSTERS 是 str）。"""
    v = CHARS.get(key, MONSTERS.get(key))
    if v is None:
        return ""
    return v[0] if isinstance(v, tuple) else v

H3DIR = ROOT / "prompts" / "h3"
H3DIR.mkdir(parents=True, exist_ok=True)

# 入选 H3 动态化的 25 镜（其余 36 镜走静帧运镜）
H3_SHOTS = ["S01", "S02", "S03", "S05", "S06", "S07", "S08", "S10", "S11", "S12", "S13",
            "S18", "S20", "S22", "S27", "S28", "S31", "S37", "S43", "S45", "S47", "S51",
            "S58", "S59"]

# 每镜的"动作节拍"：三段时间码的动作描述（0→1/3、1/3→2/3、2/3→末）
BEATS = {
    "S01": ["画面从一片空白的宣纸开始，墨色缓缓晕开，云头上一朵祥云托起金色的「九九八十一」书法大字，字体边缘有淡淡墨韵",
            "小悟空从画面右上一个跟头翻进画面，落在云头上，金箍棒在手里转了个圈，稳稳站住",
            "小悟空冲镜头咧嘴一笑，抬起一只手打招呼，祥云轻轻起伏，墨色在画面边缘继续晕开"],
    "S02": ["花果山水帘洞前，白练般的小瀑布哗哗落下，几只小猴子蹲在石桥上好奇地朝洞里张望",
            "小悟空「哗」地从水帘里蹦出来，水花溅成一片亮晶晶的小水珠，他抱着金箍棒稳稳落地",
            "小猴子们围上来又蹦又跳，小悟空得意地一甩披风，桃花瓣被风带起飘过画面"],
    "S03": ["云路之上，金顶小宫殿在远处飘着，师徒四人排成一小队从画面左侧慢慢走进来",
            "小八戒一边走一边回头看镜头招手，小白龙马甩着尾巴跟在后面，白云从他们脚边流过",
            "队伍走成一条线渐渐走远，小悟空蹦跳着跑到最前面，回头招呼大家跟上"],
    "S05": ["小悟空站在圆圆的山顶上，把金箍棒高高举起，白云从山腰飘过",
            "他耍了个花棒，棒子转成一圈金光，然后「咚」地把棒子顿在地上",
            "小悟空转身正对镜头，露出一个大大的笑脸，披风与雉鸡翎被风吹起"],
    "S06": ["草丛轻轻晃动，一只圆脸小虎妖从草里探出脑袋，瞪圆了眼睛好奇地打量",
            "小悟空一下跳到草丛前面，横举金箍棒把小虎妖挡在身后，眉毛一挑",
            "小虎妖缩了缩脖子，两只小爪子举起来，小悟空回头冲镜头耸了耸肩"],
    "S07": ["云海翻滚，小悟空踩着云头停在半空，低头看着脚下的云",
            "他用力一蹬云头，翻出一个大大的筋斗直冲上天，披风向后拉成一条线",
            "小悟空在云层上方稳稳落住，金箍棒在手里转出一圈金光，笑着看向远方"],
    "S08": ["清清的山涧水面平静，只有几圈涟漪慢慢散开，小白龙马在岸边探出头",
            "小白龙马从水里一跃而出，水花炸成一大片亮晶晶的小水珠，银白鬃毛在空中甩开",
            "它稳稳落在岸边的圆石头上，抖了抖身上的水，昂起头神气地看向镜头"],
    "S10": ["黄色的山岭上，小黄鼠妖鼓起腮帮子，肚子里憋着一口气",
            "「呼」地吹出一朵打着卷的小旋风，沙尘和几朵小黄花被卷到半空",
            "师徒四人侧着身子顶着风往前走，衣角被吹得直抖，小悟空把金箍棒插在地上当拐杖"],
    "S11": ["夜色里的小洞口挂着弯月亮，洞口垂着青藤，一盏小灯笼的光在洞口轻轻晃",
            "白白圆圆的小骨仙从洞口探出半个身子，手里提着小灯笼，眨了眨大眼睛",
            "她看到镜头，害羞地缩了缩脖子又探出来，小灯笼的光晕在夜色里晃了晃"],
    "S12": ["碧绿的小河水面上飘着几片叶子，一条红红圆圆的小金鱼在水下绕圈游",
            "小金鱼猛地跃出水面，在空中划出一道小弧线，正好跳进岸边的小竹篮里",
            "竹篮晃了晃，水花落回河里溅起小圈涟漪，小金鱼在篮里摆着尾巴看镜头"],
    "S13": ["山洞口像一朵大莲花，两个小角童肩并肩站在洞前，一起捧着一只小葫芦",
            "洞口的青藤后面，一只小九尾狐探出头来，九条蓬松的白尾巴一条条竖起来",
            "小角童得意地晃了晃小葫芦，小九尾狐跳到他们旁边甩着尾巴，三个小家伙一起看镜头"],
    "S18": ["开满小白花的树林里，小玉面狐狸姑娘轻轻捧起一枝小花凑到鼻子前",
            "她身后柿子山谷的柿子堆忽然动了动，小红蟒从柿子后面探出圆圆的脑袋",
            "小玉面狐狸姑娘回头看着小红蟒抿嘴笑，耳朵尖轻轻抖了一下，尾巴在身后慢慢摆"],
    "S20": ["小法坛上香炉冒出一串圆圆的小烟圈，三个小道士妖怪并排站在坛前",
            "三个小家伙一起抬起手，各自挥动小剑、小拂尘和小铃铛，法坛边彩旗飘起来",
            "天边升起一道小彩虹，三个小道士一起得意地挺起胸，齐刷刷看向镜头"],
    "S22": ["小悟空把金箍棒在手里转成一个金色的风车，脚下微微下沉准备发力",
            "他猛地向前一冲，围在四周的小妖怪们笑嘻嘻地四散让开，一个跟头翻到了对面",
            "小悟空单脚落地站稳，把棒子往肩上一扛，转头冲镜头挑眉一笑"],
    "S27": ["薄雾里有几朵小白花，树后静静悄悄的，什么也看不见",
            "小金斑豹从大树后面探出半个身子，圆圆的爪子搭在树干上，眼睛亮亮地看着前方",
            "它轻轻往前迈了一步，又缩回去躲在树后，只露出一对圆耳朵和一条晃动的尾巴"],
    "S28": ["灯会的小石桥上，小黄狮精抱着小钉耙撒腿就跑，脚下生风",
            "小白鼠姑娘端着一点小蜡烛跟在后面小跑，蜡烛的火苗一晃一晃，两只都在偷笑",
            "小黄狮精跑过小桥回头张望，小白鼠姑娘追上他，两人一起躲到灯柱后面探头"],
    "S31": ["竹林里两个一模一样的小悟空面对面站着，一起歪着头打量对方",
            "两人同时抬起手挠耳朵，动作完全同步，谁也不服气地瞪起眼睛",
            "左边的小悟空把棒子一横，右边的小悟空也跟着一横，两只猴子一起冲镜头做了个鬼脸"],
    "S37": ["小寺庙门前，小乌袍道士得意地叉着腰站着，身上的小圆点还是暗的",
            "他两肋下的小圆点一个一个亮起来，像一串小星星依次眨眼，金光一圈圈扩散",
            "小乌袍道士抱起手臂昂起头，身上的星光轻轻闪烁，他冲镜头眨了眨眼"],
    "S43": ["花树下师徒四人、小女王、小玉兔和小玉面狐狸姑娘挤在一起站好，谁也不动",
            "大家一起朝镜头挥手，花瓣从枝头飘落下来，小玉兔把小红绣球举高了一点",
            "花瓣落满肩头，所有人一起开心地笑起来，小八戒还偷偷比了个手势"],
    "S45": ["春天的小路上，师徒四人一马排成小队从画面左侧走进来，路边开满小花",
            "走过一棵大树的瞬间，周围的景色叠化成夏天的竹林，竹叶沙沙落下",
            "队伍继续往前走，竹林又叠化成秋天金黄的草地，四人走成一条长长的剪影"],
    "S47": ["云海之上，小悟空背对镜头站在小木桥的这一头，披风垂着不动",
            "他忽然回过头看向镜头，笑着抬起手招呼了一下，披风被风扬起一角",
            "小悟空转回身，把金箍棒往肩上一扛，大踏步走进云海深处的光里"],
    "S51": ["小悟空独自站在云头，握紧拳头，脚下云气轻轻打转",
            "他一声轻喝，身后「啪」地化出一排又一排小小的自己，转眼排成整齐方阵",
            "七十二个小悟空同时举起金箍棒，齐刷刷朝镜头笑，云头被震得抖了一下"],
    "S58": ["云海上的独木桥又细又长，桥下云气深不见底，对岸的金顶亮着柔光",
            "师徒四人一马排成一串慢慢走过木桥，桥身轻轻上下起伏，白云从桥下流过去",
            "队伍渐渐走近对岸，天光变得更亮，四个人的影子被拉长在云上"],
    "S59": ["灵山雷音寺前的草坪上，所有小伙伴都站好了，大家在互相招呼着靠拢",
            "小玉兔把花瓣往上一抛，小八戒和小悟空一起朝镜头挥手，花瓣纷纷落下来",
            "大家一起开心地笑起来，镜头缓缓后退，把所有小角色都收进画面，小铃铛和彩带轻轻晃"],
    "S60": ["水墨字卡从画面下方缓缓升起，第一行「九九八十一」的书法字先浮现",
            "后面的小字一行行跟着升起，边缘有淡淡的墨晕，背景是柔和的云海与远山",
            "字卡在画面中央停稳，镜头极缓地继续向前推，最后停住不动"],
}

CAM = {}


def load_shots():
    return {s["id"]: s for s in json.load(open("05-分镜脚本/shots.json", encoding="utf-8"))["shots"]}


def refs_for(shot):
    """返回 [(本地图路径, 描述)]，第 1 张是该镜关键帧，其后是角色设定图。"""
    out = [(f"05-分镜脚本/关键帧/可爱版/{shot['id']}.png", "该镜的关键帧（构图与画面基准）")]
    for c in shot["cast"][:2]:
        n = c if c.startswith("可爱版-") else "可爱版-" + c
        p = ROOT / "02-人物设定-可爱版" / f"{n}.png"
        if not p.exists():
            p = ROOT / "03-妖怪设定-可爱版" / f"{n}.png"
        if p.exists():
            out.append((str(p.relative_to(ROOT)), n))
    return out


def main():
    shots = load_shots()
    written = []
    for sid in H3_SHOTS:
        s = shots[sid]
        beats = BEATS.get(sid)
        if not beats:
            print("!! 缺少动作节拍:", sid); continue
        refs = refs_for(s)
        dur = s["dur"]
        t1, t2 = dur / 3, dur * 2 / 3

        def tc(t):
            return f"{int(t)//60:02d}:{int(t%60):02d}.{int(round((t % 1) * 1000)):03d}"

        cast_txt = "；".join(
            f"<Picture {i+2}> 是 {desc_of(c)}（同一角色的设定图，仅作身份与配色锚点）"
            for i, c in enumerate(s["cast"][:2]))
        pic1_line = f"<Picture 1> 是这一镜的关键帧：{s['picture']}"
        subject = ""
        for i, c in enumerate(s["cast"][:2], start=2):
            desc = desc_of(c)
            subject += f"<Subject {i-1}> 是 <Picture {i}> 中的角色：{desc}\n"

        text = f"""subject_definitions:
{pic1_line}
<Picture 1> 同时是所有出场角色的身份与配色锚点：保持关键帧里的脸型、发式、服装、配色与整体画风完全一致。
{cast_txt}
{subject}
summary:
[reference generation] 一镜到底（无剪切）的 16:9 横向镜头，{dur:.1f} 秒。**平面中国水墨绘本风格的 2D 动画**：柔和淡彩、干净粗描边、平涂上色、宣纸质感；**严禁写实化、三维化、塑料质感、真实皮肤与真实光照**，不要出现任何文字与水印。画面内容：{s['picture']}　运镜：{s['camera']}。

retention_analysis:
<Picture 1>: fully_preserved - 构图、角色造型、配色与平面水墨绘本画风在整镜内保持与关键帧一致。
<Subject 1> 及同场角色: fully_preserved - 脸型、发式、服装、配件与配色保持与参考图一致，只做自然的动作变化。

detailed_description:
[Shot 1] 平面中国水墨绘本 2D 动画，柔和淡彩，干净粗描边，宣纸质感，16:9。{s['camera']}。不要出现任何文字。
{tc(0)}-{tc(t1)}: {beats[0]}。
{tc(t1)}-{tc(t2)}: {beats[1]}。
{tc(t2)}-{tc(dur)}: {beats[2]}。
整镜只有上述角色与背景元素，没有其他人物、没有字幕、没有水印。

overall_soundscape:
轻柔的风声与极轻的铃铛/水声等环境音，音量很低，不影响唱歌。

non_diegetic_music:
None. 不要任何音乐。
"""
        p = H3DIR / f"{sid}.txt"
        p.write_text(text, encoding="utf-8")
        written.append((sid, s["dur"], len(text), [r[0] for r in refs]))

    # 生成批次清单
    manifest = []
    for sid, dur, n, refs in written:
        manifest.append({"shot": sid, "dur": round(dur, 2), "keyframe": refs[0],
                         "extra_refs": refs[1:], "prompt_file": f"prompts/h3/{sid}.txt"})
    json.dump(manifest, open("prompts/h3/batch.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1)
    print(f"已生成 {len(written)} 个 H3 提示词 → prompts/h3/")
    for sid, dur, n, refs in written:
        print(f"  {sid}  {dur:.1f}s  提示词 {n} 字  参考图 {len(refs)} 张")


if __name__ == "__main__":
    main()
