#!/usr/bin/env python3
# 步骤⑦ storyboard 引擎: 豆包 AI 拆解剧本 → 结构化分镜 JSON
# 通用版 — 支持任意剧本文件
# DEPENDS: lib/doubao_chat.py

import sys, os, json, argparse

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from lib.doubao_chat import breakdown_script


def extract_json(text):
    """从 AI 回复中提取 JSON 内容（兼容带 ```json 包裹的情况）"""
    if '```json' in text:
        return text.split('```json')[1].split('```')[0].strip()
    if '```' in text:
        return text.split('```')[1].split('```')[0].strip()
    # 尝试直接解析
    maybe = text.strip()
    if maybe.startswith('{'):
        return maybe
    return maybe


def main():
    parser = argparse.ArgumentParser(description='豆包AI → 拆解剧本为结构化分镜JSON')
    parser.add_argument('script', help='剧本文件路径 (.hdlp 或 .txt)')
    parser.add_argument('-e', '--episode', type=int, default=1, help='集号 (默认: 1)')
    parser.add_argument('-o', '--output', help='输出路径 (默认: 自动推导)')
    parser.add_argument('--pro', action='store_true', help='使用 Pro 模型 (doubao-pro, 更强更贵)')
    parser.add_argument('--dry-run', action='store_true', help='只打印将发送的提示词，不调API')
    
    args = parser.parse_args()
    
    # 读剧本
    if not os.path.isfile(args.script):
        print(f'❌ 找不到剧本文件: {args.script}')
        sys.exit(1)
    
    with open(args.script, 'r', encoding='utf-8') as f:
        script_text = f.read()
    
    print(f'📖 剧本: {args.script}')
    print(f'📐 长度: {len(script_text)} 字符')
    print(f'🎬 集号: EP{args.episode:02d}')
    print(f'🧠 模型: {"doubao-pro" if args.pro else "doubao-lite"}')
    print()
    
    if args.dry_run:
        print('🧪 DRY RUN — 将发送的提示词:')
        print('=' * 60)
        from lib.doubao_chat import breakdown_script as bd
        print(bd.__doc__)
        print('=' * 60)
        return
    
    # 调用豆包 API
    print('🚀 发送豆包 AI 拆解剧本...')
    result = breakdown_script(script_text, args.episode, model='pro' if args.pro else 'lite')
    
    if 'error' in result:
        print('❌ API ERROR:', json.dumps(result, ensure_ascii=False, indent=2))
        sys.exit(1)
    
    if 'content' not in result or not result['content']:
        print('❌ API 返回空内容:', json.dumps(result, ensure_ascii=False, indent=2))
        sys.exit(1)
    
    content = result['content']
    json_str = extract_json(content)
    
    # 解析 JSON 验证有效性
    try:
        parsed = json.loads(json_str)
        shot_count = len(parsed.get('shots', []))
    except json.JSONDecodeError as e:
        print(f'⚠️  JSON 解析失败: {e}')
        print('原始内容:')
        print(content[:500])
        shot_count = 0
        parsed = None
    
    # 确定输出路径
    if args.output:
        out_path = args.output
    else:
        # 从剧本路径自动推导
        script_dir = os.path.dirname(os.path.abspath(args.script))
        script_base = os.path.splitext(os.path.basename(args.script))[0]
        # 如果剧本文件名包含 SCRIPT 或 BREAKDOWN，替换为 STORYBOARD
        if 'SCRIPT' in script_base.upper() or 'BREAKDOWN' in script_base.upper():
            storyboard_name = script_base.replace('SCRIPT', 'STORYBOARD').replace('BREAKDOWN', 'STORYBOARD')
        else:
            model_tag = 'PRO' if args.pro else 'AI'
            storyboard_name = f'STORYBOARD-{model_tag}-EP{args.episode:02d}'
        out_path = os.path.join(script_dir, f'{storyboard_name}.json')
    
    # 保存
    with open(out_path, 'w', encoding='utf-8') as f:
        f.write(json_str if parsed else content.strip())
    
    print(f'\n✅ 分镜已保存: {out_path}')
    if parsed:
        print(f'📊 共 {shot_count} 镜')
    print(f'💰 tokens: {result.get("tokens", "N/A")}')
    print(f'⏱️  完成原因: {result.get("finish", "N/A")}')
    
    # 打印前3镜预览
    if parsed and shot_count > 0:
        print('\n📋 前3镜预览:')
        for s in parsed['shots'][:3]:
            print(f'  {s.get("shot_number","?"):>4} | {s.get("camera","?"):　<4} | {s.get("duration","?"):>2}s | {s.get("description","")[:40]}')


if __name__ == '__main__':
    main()
