#!/usr/bin/env python3
"""
Subtitle generator — SRT + HTML layer from narration timing data.

Reads per-scene timing JSON (output by tts-gen.py with subtitle_enable=true)
or falls back to narration.json subtitle arrays. Produces single-line, short
subtitles with natural break points.

Usage:
    python3 scripts/subtitle-gen.py \\
        --script narration.json \\
        --timing-dir hyperframes-output/assets/ \\
        --srt hyperframes-output/subtitles.srt \\
        --html hyperframes-output/subtitles.html \\
        --font-size 36 --color "#ffffff" --bg "rgba(0,0,0,0.6)" --position bottom
"""
import argparse
import json
import os
import re
import sys


def _srt_time(seconds):
    h = int(seconds // 3600)
    m = int((seconds % 3600) // 60)
    s = int(seconds % 60)
    ms = int((seconds - int(seconds)) * 1000)
    return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"


def split_into_short_lines(text, max_chars=16):
    """Split text into short displayable subtitle lines.
    Strips ALL punctuation — only Chinese chars, Latin letters, digits kept."""
    # Remove all characters that are NOT Chinese, letter, digit, or space
    text = re.sub(r'[^\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff'
                  r'a-zA-Z0-9\u0020]', ' ', text)
    text = re.sub(r'\s+', ' ', text).strip()

    words = text.split(' ')
    lines = []
    current = ''
    for word in words:
        if not word.strip():
            continue
        test = current + (' ' if current else '') + word
        if len(test) <= max_chars:
            current = test
        else:
            if current:
                lines.append(current.strip())
            if len(word) > max_chars:
                for i in range(0, len(word), max_chars - 1):
                    lines.append(word[i:i + max_chars - 1].strip())
            else:
                current = word
    if current.strip():
        lines.append(current.strip())
    return lines


def generate_from_timing(narration_json, timing_dir):
    """Generate subtitles using TTS API timing data (most accurate)."""
    scenes = narration_json.get("scenes", [])
    entries = []
    idx = 1

    for scene in scenes:
        scene_num = scene.get("scene", 1)
        timing_path = os.path.join(timing_dir, f"scene-{scene_num}-timing.json")

        if os.path.exists(timing_path):
            with open(timing_path) as f:
                timing_data = json.load(f)

            for seg in timing_data:
                text = seg.get("text", "").strip()
                start = seg.get("start", 0) / 1000.0  # ms → s
                end = seg.get("end", 0) / 1000.0

                # Split into displayable lines
                lines = split_into_short_lines(text)
                seg_dur = end - start
                if len(lines) == 1:
                    entries.append({
                        "text": lines[0],
                        "start": start,
                        "end": end,
                        "scene": scene_num
                    })
                elif len(lines) > 1:
                    line_dur = seg_dur / len(lines)
                    for i, line in enumerate(lines):
                        entries.append({
                            "text": line,
                            "start": start + i * line_dur,
                            "end": start + (i + 1) * line_dur,
                            "scene": scene_num
                        })
        else:
            # Fallback: auto-calculate timing from text length (~4 chars/sec CN)
            text = scene.get("narration", "")
            scene_start = scene.get("scene_start", 0)
            total_dur = scene.get("duration_estimate", max(3, len(text) / 4))

            # Split narration into natural sentences at punctuation boundaries
            import re
            sentences = re.split(r'(?<=[。！？；\n])', text)
            sentences = [s.strip() for s in sentences if s.strip()]

            if not sentences:
                sentences = [text]

            # Allocate timing proportionally by character count
            total_chars = sum(len(s) for s in sentences)
            if total_chars == 0:
                total_chars = 1

            current = scene_start
            for sent in sentences:
                line_dur = total_dur * len(sent) / total_chars
                lines = split_into_short_lines(sent)
                seg_dur = max(0.5, line_dur)
                if len(lines) == 1:
                    entries.append({"text": lines[0], "start": current,
                                   "end": current + seg_dur, "scene": scene_num})
                elif len(lines) > 1:
                    l_dur = seg_dur / len(lines)
                    for i, line in enumerate(lines):
                        entries.append({"text": line, "start": current + i * l_dur,
                                       "end": current + (i + 1) * l_dur, "scene": scene_num})
                current += seg_dur

    return entries


def generate_fallback(narration_json):
    """Fallback: generate from narration.json subtitles only."""
    return generate_from_timing(narration_json, "/nonexistent")


def generate_srt(entries, output_path):
    lines = []
    for i, e in enumerate(entries, 1):
        lines.append(f"{i}\n{_srt_time(e['start'])} --> {_srt_time(e['end'])}\n{e['text']}\n")
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write("\n".join(lines) + "\n")
    print(f"SRT: {output_path} ({len(entries)} subtitles)")


def generate_html_layer(entries, output_path, font_size=36, color="#ffffff",
                         bg="rgba(0,0,0,0.6)", position="bottom"):
    pos_css = {
        "bottom": "bottom: 80px; left: 50%; transform: translateX(-50%);",
        "top": "top: 80px; left: 50%; transform: translateX(-50%);",
        "middle": "top: 50%; left: 50%; transform: translate(-50%, -50%);",
    }.get(position, "bottom: 80px; left: 50%; transform: translateX(-50%);")

    # Build subtitle divs — ONE div per entry, shown/hidden by GSAP
    divs = []
    for e in entries:
        text = e["text"].replace('"', '&quot;').replace('<', '&lt;')
        dur = max(0.1, e["end"] - e["start"])
        divs.append(
            f'    <div class="sub" data-sub-start="{e["start"]:.2f}" '
            f'data-sub-end="{e["end"]:.2f}" style="display:none;">{text}</div>'
        )

    html = f"""<!-- Subtitle layer — generated by subtitle-gen.py -->
<div id="subtitle-layer" style="position:absolute;z-index:20;{pos_css}
     pointer-events:none;text-align:center;">
{chr(10).join(divs)}
</div>

<style>
#subtitle-layer .sub {{
  font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
  font-size: {font_size}px;
  font-weight: 700;
  color: {color};
  text-shadow: 0 2px 6px rgba(0,0,0,0.9);
  background: {bg};
  display: inline-block;
  padding: 6px 20px;
  border-radius: 6px;
  max-width: 85%;
  line-height: 1.3;
  white-space: nowrap;
}}
</style>

<script>
// Subtitle timing driven by GSAP timeline
(function() {{
  var subEls = document.querySelectorAll('#subtitle-layer .sub');
  var tlSub = gsap.timeline({{ paused: true }});

  subEls.forEach(function(el) {{
    var s = parseFloat(el.getAttribute('data-sub-start') || '0');
    var e = parseFloat(el.getAttribute('data-sub-end') || '0');
    // Show at start, hide at end — ensures only ONE line visible at a time
    tlSub.set(el, {{ display: 'inline-block', opacity: 1 }}, s);
    tlSub.set(el, {{ display: 'none', opacity: 0 }}, e);
  }});

  gsap.ticker.add(function() {{
    if (typeof tl !== 'undefined' && tlSub) tlSub.seek(tl.time());
  }});
}})();
</script>
"""

    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(html)
    print(f"HTML subtitle layer: {output_path}")


def main():
    parser = argparse.ArgumentParser(description="Generate subtitles from narration + TTS timing")
    parser.add_argument("--script", required=True, help="Path to narration.json")
    parser.add_argument("--timing-dir", default="hyperframes-output/assets/",
                        help="Directory with scene-*-timing.json files from tts-gen.py")
    parser.add_argument("--srt", default="hyperframes-output/subtitles.srt")
    parser.add_argument("--html", default="hyperframes-output/subtitles.html")
    parser.add_argument("--font-size", type=int, default=36)
    parser.add_argument("--color", default="#ffffff")
    parser.add_argument("--bg", default="rgba(0,0,0,0.6)")
    parser.add_argument("--position", default="bottom", choices=["bottom", "top", "middle"])
    args = parser.parse_args()

    with open(args.script, 'r') as f:
        data = json.load(f)

    if not data.get("scenes"):
        print("No scenes in narration.json")
        sys.exit(1)

    # Prefer TTS timing data, fallback to narration subtitles
    entries = generate_from_timing(data, args.timing_dir)
    if not entries:
        print("No subtitle entries generated")
        sys.exit(1)

    generate_srt(entries, args.srt)
    generate_html_layer(entries, args.html, args.font_size, args.color, args.bg, args.position)


if __name__ == "__main__":
    main()
