#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
桌面端封面生成入口（由 Tauri pythonscript runner 调用 __python_main）。
优先高级模板 → PIL → ffmpeg drawtext 回退。
"""
from __future__ import annotations

import json
import os
import subprocess
import sys
import tempfile
import time
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
if str(SCRIPT_DIR) not in sys.path:
    sys.path.insert(0, str(SCRIPT_DIR))

OUTPUT_DIR = Path(tempfile.gettempdir()) / "aiclient-cover"
IMAGE_EXT = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}


def _emit_progress(status: str) -> None:
    native = globals().get("__native")
    if native is not None and hasattr(native, "emit_progress"):
        native.emit_progress(status)


def _locate_ffmpeg() -> str | None:
    env = os.environ.get("AICLIENT_FFMPEG_PATH", "").strip()
    if env and os.path.isfile(env):
        return env
    return None


def _normalize_path(p: str) -> str:
    return str(p).replace("\\", "/")


def _is_image(path: str) -> bool:
    return Path(path).suffix.lower() in IMAGE_EXT


def _ensure_output_dir() -> Path:
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    return OUTPUT_DIR


def _exec_ffmpeg(args: list[str]) -> None:
    ffmpeg = _locate_ffmpeg()
    if not ffmpeg:
        raise RuntimeError(
            "未找到 ffmpeg，请配置 AICLIENT_FFMPEG_PATH 或 src-tauri/binaries/ffmpeg.exe"
        )
    r = subprocess.run(
        [ffmpeg, *args],
        capture_output=True,
        text=True,
        encoding="utf-8",
        errors="replace",
    )
    if r.returncode != 0:
        msg = (r.stderr or r.stdout or "").strip() or f"ffmpeg 退出码 {r.returncode}"
        raise RuntimeError(msg)


def _locate_cjk_font() -> str | None:
    scripts_dir = os.environ.get("AICLIENT_COVER_SCRIPTS_DIR") or str(SCRIPT_DIR)
    windir = os.environ.get("WINDIR", r"C:\Windows")
    candidates = [
        os.path.join(scripts_dir, "fonts", "simhei.ttf"),
        os.path.join(scripts_dir, "fonts", "msyh.ttc"),
        os.path.join(windir, "Fonts", "simhei.ttf"),
        os.path.join(windir, "Fonts", "msyh.ttc"),
        os.path.join(windir, "Fonts", "simsun.ttc"),
    ]
    for p in candidates:
        if p and os.path.isfile(p):
            return p
    return None


def _prepare_frame(source_path: str, temp_frame_path: str) -> None:
    if _is_image(source_path):
        _emit_progress("正在加载图片素材…")
        _exec_ffmpeg(
            [
                "-i",
                source_path,
                "-vf",
                "scale=1080:1920:force_original_aspect_ratio=decrease,"
                "pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black",
                "-y",
                temp_frame_path,
            ]
        )
        return
    _emit_progress("正在提取视频帧…")
    _exec_ffmpeg(
        [
            "-ss",
            "00:00:03",
            "-i",
            source_path,
            "-vframes",
            "1",
            "-pix_fmt",
            "yuvj420p",
            "-c:v",
            "mjpeg",
            "-strict:v",
            "unofficial",
            "-f",
            "image2",
            "-y",
            temp_frame_path,
        ]
    )


def _escape_ffmpeg_path(file_path: str) -> str:
    return file_path.replace("\\", "/").replace(":", "\\:")


def _run_ffmpeg_cover(
    source_video: str,
    title_text: str,
    effect_style: dict,
    cover_path: str,
    temp_frame_path: str,
) -> None:
    _prepare_frame(source_video, temp_frame_path)
    title_font_size = effect_style.get("titleFontSize") or 90
    title_font_color = effect_style.get("titleFontColor") or effect_style.get("titleColor") or "#FFFFFF"
    title_position = effect_style.get("titlePosition")
    if isinstance(title_position, dict):
        text_pos = "y=h-text_h-50"
    elif title_position == "top":
        text_pos = "y=50"
    elif title_position == "center":
        text_pos = "y=(h-text_h)/2"
    else:
        text_pos = "y=h-text_h-50"

    font_file = _locate_cjk_font()
    if not font_file:
        raise RuntimeError(
            "未找到中文字体文件，无法使用 ffmpeg 绘制标题"
        )

    title_file = OUTPUT_DIR / f"cover_title_{int(time.time() * 1000)}.txt"
    title_file.write_text(str(title_text), encoding="utf-8")
    try:
        font_opt = f"fontfile='{_escape_ffmpeg_path(font_file)}'"
        text_opt = f"textfile='{_escape_ffmpeg_path(str(title_file))}'"
        vf = (
            f"drawtext={font_opt}:{text_opt}:fontsize={title_font_size}:"
            f"fontcolor={title_font_color}:x=(w-text_w)/2:{text_pos}:"
            "shadowcolor=black:shadowx=2:shadowy=2"
        )
        _emit_progress("正在叠加标题文字（ffmpeg）…")
        _exec_ffmpeg(["-i", temp_frame_path, "-vf", vf, "-y", cover_path])
    finally:
        try:
            title_file.unlink(missing_ok=True)
        except OSError:
            pass


def _is_advanced_cover_config(config: dict | None) -> bool:
    if not config or not isinstance(config, dict):
        return False
    return (
        config.get("blurBackground") is not None
        or config.get("extractPerson") is not None
        or config.get("personOutlineColor") is not None
        or config.get("personOutlineWidth") is not None
        or config.get("maskImagePath") is not None
        or bool(config.get("titleFontFamily"))
        or config.get("titleBackgroundEnabled") is not None
        or config.get("personSize") is not None
        or config.get("backgroundBlurEnabled") is not None
    )


def _should_use_pil_cover(effect_style: dict) -> bool:
    if _is_advanced_cover_config(effect_style):
        return False
    return (
        bool(effect_style.get("titleStrokeWidth"))
        or bool(effect_style.get("titleFontFamily"))
        or bool(effect_style.get("titleColor"))
        or (effect_style.get("titleShadowBlur") or 0) > 0
    )


def _run_advanced(
    video_path: str,
    title_text: str,
    output_path: str,
    config: dict,
) -> dict:
    from advanced_cover_generator import AdvancedCoverGenerator

    _emit_progress("正在使用高级封面生成器…")
    merged = {
        **config,
        "output": output_path,
        "video": video_path,
        # 与 Electron 一致：CLI title 仅主标题；勿用步骤 04 整句覆盖 titleText
        "title": config.get("titleText") or title_text,
    }
    generator = AdvancedCoverGenerator(merged)
    result = generator.generate()
    if not result.get("success"):
        return {
            "success": False,
            "error": result.get("error") or result.get("message") or "高级封面生成失败",
        }
    if not os.path.isfile(output_path):
        return {"success": False, "error": "封面文件未生成"}
    return {
        "success": True,
        "coverPath": result.get("coverPath") or output_path,
        "previewImages": result.get("previewImages") or [],
        "message": result.get("message") or "高级封面生成完成",
        "mode": "advanced_python",
    }


def _run_pil(
    video_path: str,
    title_text: str,
    output_path: str,
    config: dict,
    timestamp: float = 3,
) -> dict:
    import cover_generator as cg

    _emit_progress("正在使用 PIL 封面生成器…")
    frame = cg.extract_frame_from_video(video_path, timestamp)
    if frame is None:
        return {"success": False, "error": "无法从视频提取帧"}
    width, height = frame.size
    max_width = config.get("maxWidth", 1920)
    if width > max_width:
        scale = max_width / width
        frame = frame.resize(
            (max_width, int(height * scale)),
            cg.Image.Resampling.LANCZOS,
        )
    result = cg.add_text_to_image(frame, title_text, config)
    out_dir = os.path.dirname(output_path)
    if out_dir:
        os.makedirs(out_dir, exist_ok=True)
    result.save(output_path, quality=95)
    if not os.path.isfile(output_path):
        return {"success": False, "error": "封面文件未生成"}
    return {
        "success": True,
        "coverPath": output_path,
        "message": "封面生成完成",
        "mode": "pil_python",
    }


def __python_main(params: dict | None) -> dict:
    p = params or {}
    video_path = str(p.get("videoPath") or "").strip()
    title_text = str(p.get("titleText") or "").strip()
    effect_style = p.get("effectStyle") or {}
    if not isinstance(effect_style, dict):
        effect_style = {}

    if not video_path or not os.path.isfile(video_path):
        return {
            "success": False,
            "error": "视频文件不存在，请先在步骤 02/03/05 生成视频",
        }
    if not title_text:
        return {
            "success": False,
            "error": "标题文本不能为空，请先在步骤 04 生成标题",
        }

    _ensure_output_dir()
    ts = int(time.time() * 1000)
    temp_frame_path = str(OUTPUT_DIR / f"temp_frame_{ts}.jpg")
    cover_path = str(OUTPUT_DIR / f"cover_{ts}.jpg")

    source_video = video_path
    custom = str(effect_style.get("customVideoPath") or "").strip()
    if custom and os.path.isfile(custom):
        source_video = custom

    try:
        if _is_advanced_cover_config(effect_style):
            adv = _run_advanced(source_video, title_text, cover_path, effect_style)
            if adv.get("success"):
                return adv
            _emit_progress(
                f"高级生成失败，尝试回退：{str(adv.get('error') or '')[:80]}"
            )
        elif _should_use_pil_cover(effect_style):
            pil = _run_pil(source_video, title_text, cover_path, effect_style)
            if pil.get("success"):
                return pil
            _emit_progress("PIL 生成失败，尝试 ffmpeg 回退…")

        _run_ffmpeg_cover(
            source_video, title_text, effect_style, cover_path, temp_frame_path
        )
        if os.path.isfile(temp_frame_path):
            try:
                os.unlink(temp_frame_path)
            except OSError:
                pass
        if not os.path.isfile(cover_path):
            return {"success": False, "error": "封面文件未生成"}
        return {
            "success": True,
            "coverPath": cover_path,
            "message": "封面生成完成",
            "mode": "ffmpeg",
        }
    except Exception as e:
        return {"success": False, "error": str(e) or repr(e)}
