# -*- coding: utf-8 -*-
"""从 ep-NN-seed.json 生成分镜规格 ep-NN-specs.json（分段+切法+锚点+角色提示）。"""
import json, glob, os

BASE = '/Users/cheyod/AIGC/DSH/小说2/storyboard-work'
art = json.load(open(os.path.join(BASE, 'art-digest.json'), encoding='utf-8'))
cast = json.load(open(os.path.join(BASE, 'cast-digest.json'), encoding='utf-8'))
idmap = json.load(open(os.path.join(BASE, 'idmap.json'), encoding='utf-8'))

def make_plan(beats):
    # 贪心：连续节拍凑成 <=5s 的切；切秒数 = 原始秒数和（min 2.0）
    cuts = []
    i = 0
    n = len(beats)
    while i < n:
        j = i
        raw = float(beats[i]['seconds'])
        while j + 1 < n and raw + float(beats[j+1]['seconds']) <= 6.0:
            j += 1
            raw += float(beats[j]['seconds'])
        secs = round(max(2.0, raw), 2)
        if secs > 6.0: secs = 6.0
        cuts.append((beats[i]['n'], beats[j]['n'], secs))
        i = j + 1
    return cuts

def short_anchors(scene_id):
    sc = art.get(scene_id)
    if not sc: return ''
    return '；'.join(a['name'] + '(' + a['desc'] + ')' for a in sc.get('anchors', []))

def light_en(scene_id, state):
    sc = art.get(scene_id)
    if not sc: return ''
    for lg in sc.get('lighting', []):
        if lg.get('state') == state:
            return lg.get('prompt', '')
    return ''

def char_hints(codes):
    hints = []
    for code in codes:
        name = idmap.get(code)
        if not name: continue
        c = cast.get(name, {})
        app = c.get('appearance', '') or ''
        app = app.replace(name, '').strip('，。;；、 ')
        hints.append(code + '=' + (app[:70] if app else name) + '（禁写姓名，用通用身份）')
    return '；'.join(hints)

def gen_ep(ep):
    seed = json.load(open(os.path.join(BASE, 'ep-%02d-seed.json' % ep), encoding='utf-8'))
    specs = []
    seg_idx = 1
    raw_total = 0
    for si, sc in enumerate(seed.get('seedScenes', []), 1):
        beats = sc.get('beats', [])
        if not beats: continue
        raw_total += sum(float(b['seconds']) for b in beats)
        plan = make_plan(beats)
        # 组段（<=15s）
        groups = []; cur = []; cur_sum = 0
        for cut in plan:
            if cur and cur_sum + cut[2] > 15:
                groups.append(cur); cur = []; cur_sum = 0
            cur.append(cut); cur_sum += cut[2]
        if cur: groups.append(cur)
        for g in groups:
            a0 = g[0][0]; b1 = g[-1][1]
            seg_beats = [b for b in beats if a0 <= b['n'] <= b1]
            specs.append({
                'seg': 'E%02d-%02d' % (ep, seg_idx),
                'si': si,
                'sceneId': sc.get('sceneId'),
                'light': sc.get('lighting'),
                'chars': sc.get('characters', []),
                'props': sc.get('props', []),
                'beats': [{'n': b['n'], 'kind': b.get('kind'), 'seconds': b.get('seconds'), 'text': b.get('text'), 'speaker': b.get('speaker')} for b in seg_beats],
                'plan': [list(c) for c in g],
                'anchors': short_anchors(sc.get('sceneId')),
                'lightEn': light_en(sc.get('sceneId'), sc.get('lighting')),
                'charHints': char_hints(sc.get('characters', []))
            })
            seg_idx += 1
    # 集时长不足102s时补足（+0.5s/次，封顶5s/切，总不超138）
    while sum(c[2] for s in specs for c in s['plan']) < 102.0:
        cand = [(k, ci) for k, s in enumerate(specs) for ci, c in enumerate(s['plan']) if c[2] < 5.0]
        if not cand: break
        k, ci = cand[0]
        specs[k]['plan'][ci][2] = round(specs[k]['plan'][ci][2] + 0.5, 2)
    total = sum(c[2] for s in specs for c in s['plan'])
    return specs, raw_total, total

if __name__ == '__main__':
    eps = sorted(int(os.path.basename(f)[3:5]) for f in glob.glob(os.path.join(BASE, 'ep-[0-9][0-9]-seed.json')))
    for ep in eps:
        specs, raw_total, total = gen_ep(ep)
        json.dump({'ep': ep, 'specs': specs}, open(os.path.join(BASE, 'ep-%02d-specs.json' % ep), 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
        flag = 'OK' if 102.0 <= total <= 138.0 else '***OVER/UNDER***'
        print('ep%02d segs=%2d raw=%6.1f plan=%6.1f %s' % (ep, len(specs), raw_total, total, flag))
