#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""从 draft-latest.md 重新生成 提词器.html / 逐节录制清单.html / 提词器-纯净口播版.md
用法: python3 gen_prompter.py
"""
import re, html, os, shutil, datetime
import speech_rate as SR

BASE = os.path.dirname(os.path.abspath(__file__))
DRAFT = '/root/media-hub/export/draft-latest.md'
RATE = 9947 / 2124  # 旧版全局基准（已废弃，逐节语速见 speech_rate.py）

# 画面提示类括号：从口播里删掉，不念
STRIP = [
    '（画面闪出四区报告网页）',
    '（画面停顿展示完整提示词）',
    '（切主从智能体分发动画）',
    '（切录屏 + 真机同框）',
    '（左边是电脑桌面，右边是我手里的手机微信）',
]
# 哪一节出现「（切动画…）」就标哪几个动画号（1-based 节序号）
CUE = {4: [4], 6: [5], 7: [7], 9: [8], 10: [9], 12: [11]}


def fmt(sec):
    return f'{sec // 60}:{sec % 60:02d}'


def parse():
    src = open(DRAFT, encoding='utf-8').read()
    parts = re.split(r'^【(.+?)】\s*$', src, flags=re.M)
    heads, bodies = parts[1::2], parts[2::2]
    # 原稿标题自带「1.」「2.」，去掉，避免和节序号重复成「2. 1. xxx」
    heads = [re.sub(r'^\d+\.\s*', '', h) for h in heads]
    secs = []
    for i, (h, b) in enumerate(zip(heads, bodies), 1):
        paras = []
        for line in b.split('\n'):
            line = line.strip()
            if not line:
                continue
            t = line
            cues = CUE.get(i, [])
            def repl(m):
                if not cues:
                    return ''
                return '\x00CUE\x00' * len(cues) if m.group(0) == '（切动画）' else ''
            if cues:
                # 第一条「（切动画…）」按顺序插对应动画号
                nums = list(cues)
                def repl2(m, nums=nums):
                    n = nums.pop(0) if nums else None
                    return f'\x00CUE{n}\x00' if n else ''
                t = re.sub(r'（切动画[^）]*）', repl2, t)
            for p in STRIP:
                t = t.replace(p, '')
            paras.append(t)
        plain = ''.join(paras)
        han = len(re.findall(r'[\u4e00-\u9fff]', plain.replace('\x00CUE', '')))
        secs.append({'i': i, 'title': h, 'paras': paras, 'han': han, 'sec': SR.sec_of(i, han)})
    return secs


def esc(t):
    return html.escape(t, quote=True)


def cue_spans(t):
    def r(m):
        return f'<span class="cue">✂ 动画{m.group(1)}</span>'
    return re.sub(r'\x00CUE(\d+)\x00', r, esc(t).replace('&#x27;', "'"))


def gen_prompter(secs, total_han, total_sec):
    old = open(os.path.join(BASE, '.build', 'prompter.html'), encoding='utf-8').read()
    head = old.split('<div class="wrap">')[0] + '<div class="wrap">\n'
    tail = old[old.index('</div>\n<div id="hint">'):]
    nav = '<div id="nav">' + ''.join(
        f'<button class="jump" data-go="{k}">{k + 1}</button>' for k in range(len(secs))) + '</div>\n'
    head = re.sub(r'<div id="nav">.*?</div>\n', nav, head, flags=re.S)
    out = [head]
    cum = 0
    for s in secs:
        cum += s['sec']
        out.append(f'<section class="sec" data-i="{s["i"] - 1}" data-t="{fmt(s["sec"])}" data-n="{s["han"]}" data-cum="{fmt(cum)}">\n')
        out.append(f'  <div class="sechead"><span class="sn">{s["i"]}/{len(secs)}</span><h2>{esc(s["title"])}</h2>\n')
        out.append(f'  <span class="meta">约 {fmt(s["sec"])} · {s["han"]} 字 · 累计 {fmt(cum)}</span></div>\n')
        for _j, p in enumerate(s['paras']):
            _pi = s['pis'][_j] if s.get('pis') else _j
            out.append(f'<p data-pi="{_pi}">{cue_spans(p)}</p>\n')
        out.append('\n</section>')
    out.append('\n')
    out.append(tail)
    os.makedirs(os.path.join(BASE, '.build'), exist_ok=True)
    open(os.path.join(BASE, '.build', 'prompter.html'), 'w', encoding='utf-8').write(''.join(out))


CARD1 = '''<section class="card" id="c0">
  <div class="chead"><span class="num">1</span><h2>开场与三大概念区分</h2>
  <span class="tag">{t} · {n} 字</span></div>
  <div class="cols">
    <div class="col">
      <h4>▶ 录什么</h4><div class="txt">真人出镜站着说，全片唯一纯口播开场。第一句和最后两段给真人，中间压看板画面</div>
      <h4>◆ 画面怎么走</h4><div class="txt">① 第一句「只发过一句“你好”」真人中景正面看镜头，不切画面<br>② 第一句之后按稿子里那条画面提示，切《普通人用 AI 做什么》看板 + Agent 滚屏抓评论的分屏快剪<br>③ 收尾「我是大全」回真人</div>
    </div>
    <div class="col">
      <h4>✂ 剪辑要点</h4><div class="txt">开场 {n} 字，纯念 {t}；一段一段留半拍气口，别连成一句念完</div>
      <h4>◆ 动画</h4><div class="txt anim">本段不插动画：动画2 三大概念 / 动画3 通用底座原本挂在「三种形态」那段，新开场里已经没有这段了</div>
      <h4>⚠ 坑</h4><div class="txt trap">第一句是痛点钩子，念慢一点、重音落「只发过一句你好」；「0 门槛、爷爷奶奶都能学会」是招牌句，一个字别改；「类似于 Grok Bot」照念，别改口成别的名字</div>
    </div>
  </div>
</section>'''


def gen_shootlist(secs, total_han, total_sec):
    old = open(os.path.join(BASE, 'shootlist.html'), encoding='utf-8').read()
    head = old[:old.index('<section class="card" id="c0">')]
    # 换统计数字
    head = re.sub(r'全文 \d+ 汉字', f'全文 {total_han} 汉字', head)
    head = re.sub(r'<div class="stat"><b>\d+</b><span>口播汉字</span></div>',
                  f'<div class="stat"><b>{total_han}</b><span>口播汉字</span></div>', head)
    head = re.sub(r'<div class="stat"><b>[\d:]+</b><span>纯念时长</span></div>',
                  f'<div class="stat"><b>{fmt(total_sec)}</b><span>纯念时长</span></div>', head)
    head = re.sub(r'<div class="stat"><b>约 \d+ 分</b><span>加停顿成片</span></div>',
                  f'<div class="stat"><b>约 {round(total_sec * 1.19 / 60)} 分</b><span>加停顿成片</span></div>', head)
    toc = '<div class="toc">' + ''.join(
        f'<a href="#c{k}">{k + 1}. {esc(s["title"])}</a>' for k, s in enumerate(secs)) + '</div>\n'
    head = re.sub(r'<div class="toc">.*?</div>\n', toc, head, flags=re.S)
    # 旧卡片 2~14 原样复用，只换 tag 里的时长字数
    oldcards = re.findall(r'<section class="card" id="c\d+">.*?</section>', old, re.S)
    cards = [CARD1.format(t=fmt(secs[0]['sec']), n=secs[0]['han'])]
    cards[0] = re.sub(r'<h2>.*?</h2>', f'<h2>{esc(secs[0]["title"])}</h2>', cards[0], count=1)
    for k, s in enumerate(secs[1:], 1):
        c = oldcards[k]
        c = re.sub(r'<span class="tag">[^<]*</span>',
                   f'<span class="tag">{fmt(s["sec"])} · {s["han"]} 字</span>', c)
        c = re.sub(r'<h2>.*?</h2>', f'<h2>{esc(s["title"])}</h2>', c, count=1)
        cards.append(c)
    tail = old[old.index('</div></body></html>') - 0:] if '</div></body></html>' in old else '</div></body></html>'
    open(os.path.join(BASE, 'shootlist.html'), 'w', encoding='utf-8').write(head + ''.join(cards) + '\n' + tail)


def gen_clean_md(secs, total_han, total_sec):
    d = datetime.date.today().isoformat()
    out = [f'# 大全 WorkBuddy 大课 · 完整口播文案（{d} 封版）\n',
           f'> {len(secs)} 节 · {total_han} 口播汉字 · 纯念 {fmt(total_sec)} · 加停顿成片约 {round(total_sec * 1.19 / 60)} 分\n',
           '> 只删掉了画面提示类括号，正文一字未改。原稿：`/root/media-hub/export/draft-latest.md`\n']
    for s in secs:
        out.append(f'## {s["i"]}. {s["title"]}　（约 {fmt(s["sec"])} · {s["han"]} 字）\n')
        for p in s['paras']:
            out.append(re.sub(r'\x00CUE\d*\x00', '', p) + '\n')
        out.append('')
    open(os.path.join(BASE, '提词器-纯净口播版.md'), 'w', encoding='utf-8').write('\n'.join(out))


if __name__ == '__main__':
    stamp = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
    for f in ['shootlist.html', '提词器-纯净口播版.md']:
        p = os.path.join(BASE, f)
        if os.path.exists(p):
            shutil.copy2(p, os.path.join(BASE, 'backup', f'{f}.{stamp}.bak')) if os.path.isdir(os.path.join(BASE, 'backup')) else os.makedirs(os.path.join(BASE, 'backup'), exist_ok=True) or shutil.copy2(p, os.path.join(BASE, 'backup', f'{f}.{stamp}.bak'))
    secs = parse()
    total_han = sum(s['han'] for s in secs)
    total_sec = sum(s['sec'] for s in secs)
    gen_prompter(secs, total_han, total_sec)
    gen_shootlist(secs, total_han, total_sec)
    gen_clean_md(secs, total_han, total_sec)
    print('sections', len(secs), 'han', total_han, 'time', fmt(total_sec))
