#!/usr/bin/env python3
"""Generate TTS narration for the demo video using edge-tts.
Uses XiaoxiaoNeural with natural pacing and SSML prosody control."""

import asyncio
import sys
import os
import struct
import wave

OUTPUT_DIR = sys.argv[1] if len(sys.argv) > 1 else "demo-output"

# (start_seconds, text, [optional: pause_after_ms])
# Timeline aligned with demo-subtitles.srt and demo-walkthrough.cy.js
SEGMENTS = [
    (0.5,  '欢迎使用 doTERRA 精油配方计算器。这是一个功能完整的精油配方管理工具。'),
    (5.0,  '首页展示了所有公共配方，以卡片形式排列。'),
    (9.0,  '可以上下滚动，浏览更多配方。'),
    (12.0, '在搜索框输入关键词，可以快速筛选配方。'),
    (16.0, '输入薰衣草，立即过滤出包含薰衣草的配方。'),
    (20.0, '点击任意配方卡片，查看配方详情。'),
    (24.5, '详情页显示精油成分、用量、单价、和总成本。'),
    (30.0, '切换到精油价目页面，查看所有精油价格。'),
    (34.5, '支持按名称搜索精油。'),
    (38.0, '可以切换瓶价和滴价两种显示模式。'),
    (42.0, '管理配方页面，用于配方的增删改查和批量操作。'),
    (47.0, '支持标签筛选、批量打标签、批量导出卡片。'),
    (52.0, '个人库存页面，标记自己拥有的精油。'),
    (56.5, '系统会自动推荐你能制作的配方。'),
    (60.0, '操作日志记录所有数据变更，支持按类型和用户筛选。'),
    (66.0, '内置Bug追踪系统，支持优先级、状态流转和评论。'),
    (72.0, '用户管理可以创建用户、分配角色、审核翻译建议。'),
    (78.0, '最后回到首页。演示到此结束。'),
    (82.5, '感谢观看！这是基于Vue 3、Vite和Pinia构建的现代化应用。'),
]

VOICE = "zh-CN-XiaoxiaoNeural"
RATE = "+0%"
PITCH = "+0Hz"

try:
    import edge_tts
except ImportError:
    print("Installing edge-tts...")
    os.system(f"{sys.executable} -m pip install edge-tts -q")
    import edge_tts


async def generate_segment(text, output_path):
    """Generate a single TTS segment with natural prosody."""
    communicate = edge_tts.Communicate(text, VOICE, rate=RATE, pitch=PITCH)
    await communicate.save(output_path)


async def main():
    os.makedirs(OUTPUT_DIR, exist_ok=True)
    segment_files = []

    print(f"Generating {len(SEGMENTS)} TTS segments with {VOICE}...")

    for i, (start_sec, text) in enumerate(SEGMENTS):
        seg_path = os.path.join(OUTPUT_DIR, f"seg_{i:02d}.mp3")
        print(f"  [{i+1}/{len(SEGMENTS)}] {start_sec:5.1f}s  {text[:40]}...")
        await generate_segment(text, seg_path)
        segment_files.append((start_sec, seg_path))

    # Total duration: last segment start + 8s buffer
    total_duration = SEGMENTS[-1][0] + 8

    print(f"\nAssembling narration track ({total_duration:.0f}s)...")

    # Generate silence base
    silence_path = os.path.join(OUTPUT_DIR, "silence.mp3")
    os.system(
        f'ffmpeg -y -f lavfi -i anullsrc=r=44100:cl=mono '
        f'-t {total_duration} -c:a libmp3lame -q:a 2 "{silence_path}" 2>/dev/null'
    )

    # Build ffmpeg complex filter to overlay each segment at its timestamp
    inputs = [f'-i "{silence_path}"']
    filter_parts = []

    for i, (start_sec, seg_path) in enumerate(segment_files):
        inputs.append(f'-i "{seg_path}"')
        delay_ms = int(start_sec * 1000)
        # Normalize volume for each segment
        filter_parts.append(f'[{i+1}]adelay={delay_ms}|{delay_ms},volume=1.5[d{i}]')

    # Mix all
    mix_inputs = "[0]" + "".join(f"[d{i}]" for i in range(len(segment_files)))
    n_inputs = len(segment_files) + 1
    filter_parts.append(
        f'{mix_inputs}amix=inputs={n_inputs}:duration=longest:dropout_transition=0,'
        f'volume={n_inputs}[out]'
    )

    filter_str = ";".join(filter_parts)
    output_path = os.path.join(OUTPUT_DIR, "narration.mp3")

    cmd = (
        f'ffmpeg -y {" ".join(inputs)} '
        f'-filter_complex "{filter_str}" '
        f'-map "[out]" -c:a libmp3lame -q:a 2 "{output_path}" 2>/dev/null'
    )

    ret = os.system(cmd)
    if ret != 0:
        print("ERROR: ffmpeg narration assembly failed")
        print("Trying fallback: simple concatenation...")
        # Fallback: just concatenate with silence gaps
        concat_list = os.path.join(OUTPUT_DIR, "concat.txt")
        with open(concat_list, "w") as f:
            prev_end = 0
            for start_sec, seg_path in segment_files:
                gap = start_sec - prev_end
                if gap > 0:
                    gap_path = os.path.join(OUTPUT_DIR, f"gap_{prev_end:.0f}.mp3")
                    os.system(f'ffmpeg -y -f lavfi -i anullsrc=r=44100:cl=mono -t {gap} -c:a libmp3lame -q:a 2 "{gap_path}" 2>/dev/null')
                    f.write(f"file '{gap_path}'\n")
                f.write(f"file '{seg_path}'\n")
                # Estimate segment duration ~3s
                prev_end = start_sec + 3
        os.system(f'ffmpeg -y -f concat -safe 0 -i "{concat_list}" -c:a libmp3lame -q:a 2 "{output_path}" 2>/dev/null')

    # Cleanup
    for _, seg_path in segment_files:
        try: os.remove(seg_path)
        except: pass
    try: os.remove(silence_path)
    except: pass

    print(f"\nDone! Output: {output_path}")
    if os.path.exists(output_path):
        size = os.path.getsize(output_path) / 1024
        print(f"Size: {size:.0f} KB")
    else:
        print("ERROR: Output file not created")
        sys.exit(1)


if __name__ == "__main__":
    asyncio.run(main())
