"""
Remotion 视频渲染服务
调用 Node.js Remotion 进行视频合成（字幕 + 标题）
"""

import asyncio
import json
import os
import subprocess
from collections.abc import Callable
from pathlib import Path
from typing import Optional
from loguru import logger


class RemotionService:
    """Remotion 视频渲染服务"""

    def __init__(self, remotion_dir: Optional[str] = None):
        # Remotion 项目目录
        if remotion_dir:
            self.remotion_dir = Path(remotion_dir)
        else:
            # 默认在 ViGent2/remotion 目录
            self.remotion_dir = Path(__file__).parent.parent.parent.parent / "remotion"

    async def render(
        self,
        video_path: str,
        output_path: str,
        captions_path: Optional[str] = None,
        title: Optional[str] = None,
        title_duration: float = 4.0,
        title_display_mode: str = "short",
        fps: int = 25,
        enable_subtitles: bool = True,
        subtitle_style: Optional[dict] = None,
        title_style: Optional[dict] = None,
        secondary_title: Optional[str] = None,
        secondary_title_style: Optional[dict] = None,
        on_progress: Optional[Callable[[int], None]] = None
    ) -> str:
        """
        使用 Remotion 渲染视频（添加字幕和标题）

        Args:
            video_path: 输入视频路径（唇形同步后的视频）
            output_path: 输出视频路径
            captions_path: 字幕 JSON 文件路径（Whisper 生成）
            title: 视频标题（可选）
            title_duration: 标题显示时长（秒）
            title_display_mode: 标题显示模式（short/persistent）
            fps: 帧率
            enable_subtitles: 是否启用字幕
            on_progress: 进度回调函数

        Returns:
            输出视频路径
        """
        # 构建命令参数
        # 优先使用预编译的 JS 文件（更快），如果不存在则回退到 ts-node
        compiled_js = self.remotion_dir / "dist" / "render.js"
        if compiled_js.exists():
            cmd = ["node", "dist/render.js"]
            logger.info("Using pre-compiled render.js for faster startup")
        else:
            cmd = ["npx", "ts-node", "render.ts"]
            logger.warning("Using ts-node (slower). Run 'npm run build:render' to compile for faster startup.")
        
        cmd.extend([
            "--video", str(video_path),
            "--output", str(output_path),
            "--fps", str(fps),
            "--enableSubtitles", str(enable_subtitles).lower(),
            "--concurrency", "4"
        ])

        if captions_path:
            cmd.extend(["--captions", str(captions_path)])

        if title:
            cmd.extend(["--title", title])
            cmd.extend(["--titleDuration", str(title_duration)])
            cmd.extend(["--titleDisplayMode", title_display_mode])

        if subtitle_style:
            cmd.extend(["--subtitleStyle", json.dumps(subtitle_style, ensure_ascii=False)])

        if title_style:
            cmd.extend(["--titleStyle", json.dumps(title_style, ensure_ascii=False)])

        if secondary_title:
            cmd.extend(["--secondaryTitle", secondary_title])

        if secondary_title_style:
            cmd.extend(["--secondaryTitleStyle", json.dumps(secondary_title_style, ensure_ascii=False)])

        logger.info(f"Running Remotion render: {' '.join(cmd)}")

        # 在线程池中运行子进程
        def _run_render():
            process = subprocess.Popen(
                cmd,
                cwd=str(self.remotion_dir),
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1
            )

            if process.stdout is None:
                raise RuntimeError("Remotion process stdout is unavailable")
            stdout = process.stdout

            output_lines = []
            for line in iter(stdout.readline, ''):
                line = line.strip()
                if line:
                    output_lines.append(line)
                    logger.debug(f"[Remotion] {line}")

                    # 解析进度
                    if "Rendering:" in line and "%" in line:
                        try:
                            percent_str = line.split("Rendering:")[1].strip().replace("%", "")
                            percent = int(percent_str)
                            if on_progress:
                                on_progress(percent)
                        except (ValueError, IndexError):
                            pass

            process.wait()

            if process.returncode != 0:
                # Remotion 渲染可能在完成输出后进程崩溃 (如 SIGABRT code -6)
                # 如果输出文件已存在且大小合理，视为成功
                output_file = Path(output_path)
                if output_file.exists() and output_file.stat().st_size > 1024:
                    logger.warning(
                        f"Remotion process exited with code {process.returncode}, "
                        f"but output file exists ({output_file.stat().st_size} bytes). Treating as success."
                    )
                    return output_path

                error_msg = "\n".join(output_lines[-20:])  # 最后 20 行
                raise RuntimeError(f"Remotion render failed (code {process.returncode}):\n{error_msg}")

            return output_path

        loop = asyncio.get_event_loop()
        result = await loop.run_in_executor(None, _run_render)

        logger.info(f"Remotion render complete: {result}")
        return result

    async def check_health(self) -> dict:
        """检查 Remotion 服务健康状态"""
        try:
            # 检查 remotion 目录是否存在
            if not self.remotion_dir.exists():
                return {
                    "ready": False,
                    "error": f"Remotion directory not found: {self.remotion_dir}"
                }

            # 检查 package.json 是否存在
            package_json = self.remotion_dir / "package.json"
            if not package_json.exists():
                return {
                    "ready": False,
                    "error": "package.json not found"
                }

            # 检查 node_modules 是否存在
            node_modules = self.remotion_dir / "node_modules"
            if not node_modules.exists():
                return {
                    "ready": False,
                    "error": "node_modules not found, run 'npm install' first"
                }

            return {
                "ready": True,
                "remotion_dir": str(self.remotion_dir)
            }

        except Exception as e:
            return {
                "ready": False,
                "error": str(e)
            }


# 全局服务实例
remotion_service = RemotionService()
