use anyhow::{Result, anyhow};
use serde_json::Value;
use std::process::Command;
use std::path::Path;
use crate::data::models::material::{VideoMetadata, AudioMetadata, MaterialMetadata};
use tracing::error;

#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;

/// FFmpeg 工具集成
/// 遵循 Tauri 开发规范的基础设施层设计
pub struct FFmpegService;

impl FFmpegService {
    /// 创建隐藏控制台窗口的命令
    /// 在 Windows 上防止命令行闪现
    fn create_hidden_command(program: &str) -> Command {
        let mut cmd = Command::new(program);

        #[cfg(target_os = "windows")]
        {
            // 在 Windows 上隐藏控制台窗口
            // CREATE_NO_WINDOW = 0x08000000
            cmd.creation_flags(0x08000000);
        }

        cmd
    }

    /// 检查 FFmpeg 是否可用
    pub fn is_available() -> bool {
        let ffmpeg_available = Self::create_hidden_command("ffmpeg")
            .arg("-version")
            .output()
            .map(|output| output.status.success())
            .unwrap_or(false);

        let ffprobe_available = Self::create_hidden_command("ffprobe")
            .arg("-version")
            .output()
            .map(|output| output.status.success())
            .unwrap_or(false);

        ffmpeg_available && ffprobe_available
    }

    /// 获取详细的FFmpeg状态信息
    pub fn get_status_info() -> Result<String> {
        let mut info = String::new();

        // 检查 ffmpeg
        match Self::create_hidden_command("ffmpeg").arg("-version").output() {
            Ok(output) if output.status.success() => {
                let version_str = String::from_utf8_lossy(&output.stdout);
                if let Some(first_line) = version_str.lines().next() {
                    info.push_str(&format!("FFmpeg: {}\n", first_line));
                }
            }
            Ok(_) => info.push_str("FFmpeg: 命令执行失败\n"),
            Err(e) => info.push_str(&format!("FFmpeg: 未找到 ({})\n", e)),
        }

        // 检查 ffprobe
        match Self::create_hidden_command("ffprobe").arg("-version").output() {
            Ok(output) if output.status.success() => {
                let version_str = String::from_utf8_lossy(&output.stdout);
                if let Some(first_line) = version_str.lines().next() {
                    info.push_str(&format!("FFprobe: {}\n", first_line));
                }
            }
            Ok(_) => info.push_str("FFprobe: 命令执行失败\n"),
            Err(e) => info.push_str(&format!("FFprobe: 未找到 ({})\n", e)),
        }

        Ok(info)
    }

    /// 提取视频/音频元数据
    pub fn extract_metadata(file_path: &str) -> Result<MaterialMetadata> {
        if !Path::new(file_path).exists() {
            return Err(anyhow!("文件不存在: {}", file_path));
        }

        let output = Self::create_hidden_command("ffprobe")
            .args([
                "-v", "quiet",
                "-print_format", "json",
                "-show_format",
                "-show_streams",
                file_path
            ])
            .output()
            .map_err(|e| anyhow!("执行 ffprobe 失败: {}", e))?;

        if !output.status.success() {
            let error_msg = String::from_utf8_lossy(&output.stderr);
            return Err(anyhow!("ffprobe 执行失败: {}", error_msg));
        }

        let json_str = String::from_utf8_lossy(&output.stdout);
        let json: Value = serde_json::from_str(&json_str)
            .map_err(|e| anyhow!("解析 ffprobe 输出失败: {}", e))?;

        Self::parse_metadata(&json)
    }

    /// 解析 ffprobe 输出的 JSON 数据
    fn parse_metadata(json: &Value) -> Result<MaterialMetadata> {
        let format = json.get("format")
            .ok_or_else(|| anyhow!("缺少 format 信息"))?;
        
        let streams = json.get("streams")
            .and_then(|s| s.as_array())
            .ok_or_else(|| anyhow!("缺少 streams 信息"))?;

        // 查找视频流
        let video_stream = streams.iter()
            .find(|stream| stream.get("codec_type").and_then(|t| t.as_str()) == Some("video"));

        // 查找音频流
        let audio_stream = streams.iter()
            .find(|stream| stream.get("codec_type").and_then(|t| t.as_str()) == Some("audio"));

        if let Some(video) = video_stream {
            // 这是一个视频文件
            let duration = format.get("duration")
                .and_then(|d| d.as_str())
                .and_then(|s| s.parse::<f64>().ok())
                .unwrap_or(0.0);

            let width = video.get("width")
                .and_then(|w| w.as_u64())
                .unwrap_or(0) as u32;

            let height = video.get("height")
                .and_then(|h| h.as_u64())
                .unwrap_or(0) as u32;

            let fps = video.get("r_frame_rate")
                .and_then(|fps| fps.as_str())
                .and_then(|s| Self::parse_fraction(s))
                .unwrap_or(0.0);

            let bitrate = format.get("bit_rate")
                .and_then(|b| b.as_str())
                .and_then(|s| s.parse::<u64>().ok())
                .unwrap_or(0);

            let codec = video.get("codec_name")
                .and_then(|c| c.as_str())
                .unwrap_or("unknown")
                .to_string();

            let format_name = format.get("format_name")
                .and_then(|f| f.as_str())
                .unwrap_or("unknown")
                .to_string();

            let has_audio = audio_stream.is_some();
            let audio_codec = audio_stream
                .and_then(|a| a.get("codec_name"))
                .and_then(|c| c.as_str())
                .map(|s| s.to_string());

            let audio_bitrate = audio_stream
                .and_then(|a| a.get("bit_rate"))
                .and_then(|b| b.as_str())
                .and_then(|s| s.parse::<u64>().ok());

            let audio_sample_rate = audio_stream
                .and_then(|a| a.get("sample_rate"))
                .and_then(|r| r.as_str())
                .and_then(|s| s.parse::<u32>().ok());

            Ok(MaterialMetadata::Video(VideoMetadata {
                duration,
                width,
                height,
                fps,
                bitrate,
                codec,
                format: format_name,
                has_audio,
                audio_codec,
                audio_bitrate,
                audio_sample_rate,
            }))

        } else if let Some(audio) = audio_stream {
            // 这是一个音频文件
            let duration = format.get("duration")
                .and_then(|d| d.as_str())
                .and_then(|s| s.parse::<f64>().ok())
                .unwrap_or(0.0);

            let bitrate = format.get("bit_rate")
                .and_then(|b| b.as_str())
                .and_then(|s| s.parse::<u64>().ok())
                .unwrap_or(0);

            let codec = audio.get("codec_name")
                .and_then(|c| c.as_str())
                .unwrap_or("unknown")
                .to_string();

            let sample_rate = audio.get("sample_rate")
                .and_then(|r| r.as_str())
                .and_then(|s| s.parse::<u32>().ok())
                .unwrap_or(0);

            let channels = audio.get("channels")
                .and_then(|c| c.as_u64())
                .unwrap_or(0) as u32;

            let format_name = format.get("format_name")
                .and_then(|f| f.as_str())
                .unwrap_or("unknown")
                .to_string();

            Ok(MaterialMetadata::Audio(AudioMetadata {
                duration,
                bitrate,
                codec,
                sample_rate,
                channels,
                format: format_name,
            }))

        } else {
            Err(anyhow!("未找到有效的视频或音频流"))
        }
    }

    /// 解析分数格式的帧率 (如 "30/1")
    fn parse_fraction(fraction_str: &str) -> Option<f64> {
        let parts: Vec<&str> = fraction_str.split('/').collect();
        if parts.len() == 2 {
            if let (Ok(numerator), Ok(denominator)) = (parts[0].parse::<f64>(), parts[1].parse::<f64>()) {
                if denominator != 0.0 {
                    return Some(numerator / denominator);
                }
            }
        }
        None
    }

    /// 创建跳过开头指定毫秒数的临时视频文件
    /// 返回临时文件路径，调用者负责清理
    pub fn create_trimmed_video(input_path: &str, skip_start_ms: u32) -> Result<String> {
        if !Path::new(input_path).exists() {
            return Err(anyhow!("输入文件不存在: {}", input_path));
        }

        if skip_start_ms == 0 {
            // 如果不需要跳过，直接返回原文件路径
            return Ok(input_path.to_string());
        }

        // 创建临时文件路径
        let input_path_obj = Path::new(input_path);
        let file_stem = input_path_obj.file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("temp");
        let extension = input_path_obj.extension()
            .and_then(|s| s.to_str())
            .unwrap_or("mp4");

        let temp_dir = std::env::temp_dir();
        let temp_file = temp_dir.join(format!("{}_trimmed_{}.{}", file_stem, skip_start_ms, extension));
        let temp_path = temp_file.to_string_lossy().to_string();

        println!("创建跳过前{}ms的临时视频: {} -> {}", skip_start_ms, input_path, temp_path);

        // 使用FFmpeg跳过开头指定毫秒数
        let skip_seconds = skip_start_ms as f64 / 1000.0;
        let output = Self::create_hidden_command("ffmpeg")
            .args([
                "-i", input_path,
                "-ss", &skip_seconds.to_string(),  // 跳过开头
                "-c", "copy",                      // 流复制，避免重新编码
                "-avoid_negative_ts", "make_zero", // 避免负时间戳
                "-y",                              // 覆盖输出文件
                &temp_path
            ])
            .output()
            .map_err(|e| anyhow!("执行视频跳过失败: {}", e))?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(anyhow!("FFmpeg视频跳过失败: {}", stderr));
        }

        // 验证输出文件是否存在
        if !Path::new(&temp_path).exists() {
            return Err(anyhow!("临时视频文件创建失败: {}", temp_path));
        }

        println!("临时视频创建成功: {}", temp_path);
        Ok(temp_path)
    }

    /// 检测视频场景变化
    pub fn detect_scenes(file_path: &str, threshold: f64) -> Result<Vec<f64>> {
        if !Path::new(file_path).exists() {
            return Err(anyhow!("文件不存在: {}", file_path));
        }

        println!("开始场景检测: {} (阈值: {})", file_path, threshold);

        // 首先尝试使用 ffmpeg 的 scene 滤镜
        match Self::detect_scenes_with_ffmpeg(file_path, threshold) {
            Ok(scenes) if !scenes.is_empty() => {
                println!("FFmpeg场景检测成功，发现 {} 个场景: {:?}", scenes.len(), scenes);
                return Ok(scenes);
            }
            Err(e) => {
                println!("FFmpeg场景检测失败，使用备用方法: {}", e);
            }
            Ok(_) => {
                println!("FFmpeg场景检测返回空结果，使用备用方法");
            }
        }

        // 如果FFmpeg场景检测失败，使用简单的时间间隔方法
        let simple_scenes = Self::detect_scenes_simple(file_path, threshold)?;
        println!("备用场景检测完成，发现 {} 个场景: {:?}", simple_scenes.len(), simple_scenes);
        Ok(simple_scenes)
    }

    /// 使用FFmpeg进行场景检测
    fn detect_scenes_with_ffmpeg(file_path: &str, threshold: f64) -> Result<Vec<f64>> {
        // 方法1: 使用 scene 滤镜和 showinfo
        let output1 = Self::create_hidden_command("ffmpeg")
            .args([
                "-i", file_path,
                "-vf", &format!("select='gt(scene,{})',showinfo", threshold),
                "-f", "null",
                "-"
            ])
            .stderr(std::process::Stdio::piped())
            .output();

        if let Ok(output) = output1 {
            let stderr_str = String::from_utf8_lossy(&output.stderr);
            let mut scene_times = Vec::new();

            // 查找 showinfo 输出中的 pts_time 信息
            for line in stderr_str.lines() {
                if line.contains("showinfo") && line.contains("pts_time:") {
                    if let Some(pts_start) = line.find("pts_time:") {
                        let pts_part = &line[pts_start + 9..];
                        if let Some(space_pos) = pts_part.find(' ') {
                            let time_str = &pts_part[..space_pos];
                            if let Ok(time) = time_str.parse::<f64>() {
                                scene_times.push(time);
                            }
                        }
                    }
                }
            }

            if !scene_times.is_empty() {
                return Ok(scene_times);
            }
        }

        // 方法2: 使用更简单的场景检测方法
        Self::detect_scenes_alternative(file_path, threshold)
    }

    /// 替代的场景检测方法
    fn detect_scenes_alternative(file_path: &str, _threshold: f64) -> Result<Vec<f64>> {
        // 使用 ffprobe 分析视频帧信息
        let output = Self::create_hidden_command("ffprobe")
            .args([
                "-v", "quiet",
                "-select_streams", "v:0",
                "-show_entries", "frame=pkt_pts_time,pict_type",
                "-of", "csv=p=0",
                file_path
            ])
            .output()
            .map_err(|e| anyhow!("执行ffprobe帧分析失败: {}", e))?;

        if !output.status.success() {
            let error_msg = String::from_utf8_lossy(&output.stderr);
            return Err(anyhow!("ffprobe帧分析失败: {}", error_msg));
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        let mut scene_times = Vec::new();
        let mut last_i_frame_time = 0.0;
        let min_scene_duration = 5.0; // 最小场景时长5秒

        // 分析I帧（关键帧）作为潜在的场景切换点
        for line in output_str.lines() {
            let parts: Vec<&str> = line.split(',').collect();
            if parts.len() >= 2 {
                if let (Ok(time), pict_type) = (parts[0].parse::<f64>(), parts[1]) {
                    if pict_type == "I" && time - last_i_frame_time > min_scene_duration {
                        scene_times.push(time);
                        last_i_frame_time = time;
                    }
                }
            }
        }

        Ok(scene_times)
    }

    /// 简单的场景检测方法（备用）
    fn detect_scenes_simple(file_path: &str, threshold: f64) -> Result<Vec<f64>> {
        // 使用 ffprobe 获取视频时长，然后按智能间隔分割
        let metadata = Self::extract_metadata(file_path)?;

        let duration = match metadata {
            MaterialMetadata::Video(video_meta) => video_meta.duration,
            _ => return Ok(Vec::new()),
        };

        // 如果视频很短，不需要场景检测
        if duration < 30.0 {
            return Ok(Vec::new());
        }

        let mut scene_times = Vec::new();

        // 根据视频时长和阈值智能确定切分策略
        if duration <= 120.0 {
            // 2分钟以内的视频，按30秒间隔
            let mut current_time = 30.0;
            while current_time < duration {
                scene_times.push(current_time);
                current_time += 30.0;
            }
        } else if duration <= 600.0 {
            // 10分钟以内的视频，按60秒间隔
            let mut current_time = 60.0;
            while current_time < duration {
                scene_times.push(current_time);
                current_time += 60.0;
            }
        } else {
            // 长视频，按120秒间隔
            let mut current_time = 120.0;
            while current_time < duration {
                scene_times.push(current_time);
                current_time += 120.0;
            }
        }

        // 如果阈值很低（更敏感），增加更多切点
        if threshold < 0.2 && duration > 180.0 {
            let mut additional_times = Vec::new();
            for &time in &scene_times {
                if time > 90.0 {
                    additional_times.push(time - 45.0);
                }
            }
            scene_times.extend(additional_times);
            scene_times.sort_by(|a, b| a.partial_cmp(b).unwrap());
        }

        Ok(scene_times)
    }

    /// 切分视频（重新编码模式，确保画面完整）
    pub fn split_video(
        input_path: &str,
        output_dir: &str,
        segments: &[(f64, f64)], // (start_time, end_time) pairs
        output_prefix: &str,
    ) -> Result<Vec<String>> {
        Self::split_video_with_mode(input_path, output_dir, segments, output_prefix, false)
    }

    /// 快速切分视频（流复制模式，速度快但可能有画面问题）
    pub fn split_video_fast(
        input_path: &str,
        output_dir: &str,
        segments: &[(f64, f64)], // (start_time, end_time) pairs
        output_prefix: &str,
    ) -> Result<Vec<String>> {
        Self::split_video_with_mode(input_path, output_dir, segments, output_prefix, true)
    }

    /// 切分视频的内部实现
    fn split_video_with_mode(
        input_path: &str,
        output_dir: &str,
        segments: &[(f64, f64)], // (start_time, end_time) pairs
        output_prefix: &str,
        use_copy_mode: bool,
    ) -> Result<Vec<String>> {
        if !Path::new(input_path).exists() {
            return Err(anyhow!("输入文件不存在: {}", input_path));
        }

        std::fs::create_dir_all(output_dir)
            .map_err(|e| anyhow!("创建输出目录失败: {}", e))?;

        let mut output_files = Vec::new();

        for (index, (start_time, end_time)) in segments.iter().enumerate() {
            let duration = end_time - start_time;
            let output_file = format!("{}/{}_{:03}.mp4", output_dir, output_prefix, index + 1);

            println!("切分视频片段 {}: {}s - {}s (时长: {}s) [模式: {}]",
                index + 1, start_time, end_time, duration,
                if use_copy_mode { "快速复制" } else { "重新编码" });

            let output = if use_copy_mode {
                // 快速模式：流复制（可能有前几秒无画面问题）
                Self::create_hidden_command("ffmpeg")
                    .args([
                        "-i", input_path,
                        "-ss", &start_time.to_string(),
                        "-t", &duration.to_string(),
                        "-c", "copy",
                        "-avoid_negative_ts", "make_zero",
                        "-y",
                        &output_file
                    ])
                    .output()
                    .map_err(|e| anyhow!("执行视频切分失败: {}", e))?
            } else {
                // 重新编码模式：确保切分点准确，避免前几秒无画面问题
                Self::create_hidden_command("ffmpeg")
                    .args([
                        "-i", input_path,
                        "-ss", &start_time.to_string(),
                        "-t", &duration.to_string(),
                        "-c:v", "libx264",           // 重新编码视频
                        "-c:a", "aac",               // 重新编码音频
                        "-preset", "fast",           // 编码速度设置
                        "-crf", "23",                // 质量设置（0-51，越小质量越好）
                        "-avoid_negative_ts", "make_zero",
                        "-movflags", "+faststart",   // 优化网络播放
                        "-y",                        // 覆盖输出文件
                        &output_file
                    ])
                    .output()
                    .map_err(|e| anyhow!("执行视频切分失败: {}", e))?
            };

            if !output.status.success() {
                let error_msg = String::from_utf8_lossy(&output.stderr);
                return Err(anyhow!("视频切分失败 (片段 {}): {}", index + 1, error_msg));
            }

            println!("片段 {} 切分完成: {}", index + 1, output_file);
            output_files.push(output_file);
        }

        Ok(output_files)
    }

    /// 智能切分视频（寻找最近的关键帧）
    pub fn split_video_smart(
        input_path: &str,
        output_dir: &str,
        segments: &[(f64, f64)], // (start_time, end_time) pairs
        output_prefix: &str,
    ) -> Result<Vec<String>> {
        // 首先获取关键帧信息
        let keyframes = Self::get_keyframes(input_path)?;

        // 调整切分点到最近的关键帧
        let adjusted_segments: Vec<(f64, f64)> = segments.iter().map(|(start, end)| {
            let adjusted_start = Self::find_nearest_keyframe(&keyframes, *start);
            let adjusted_end = Self::find_nearest_keyframe(&keyframes, *end);
            (adjusted_start, adjusted_end)
        }).collect();

        println!("原始切分点: {:?}", segments);
        println!("调整后切分点: {:?}", adjusted_segments);

        // 使用调整后的切分点进行快速切分
        Self::split_video_fast(input_path, output_dir, &adjusted_segments, output_prefix)
    }

    /// 获取视频的关键帧时间点
    fn get_keyframes(input_path: &str) -> Result<Vec<f64>> {
        let output = Self::create_hidden_command("ffprobe")
            .args([
                "-v", "quiet",
                "-select_streams", "v:0",
                "-show_entries", "frame=pkt_pts_time",
                "-of", "csv=p=0",
                "-skip_frame", "nokey",  // 只显示关键帧
                input_path
            ])
            .output()
            .map_err(|e| anyhow!("获取关键帧信息失败: {}", e))?;

        if !output.status.success() {
            let error_msg = String::from_utf8_lossy(&output.stderr);
            return Err(anyhow!("获取关键帧失败: {}", error_msg));
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        let mut keyframes = Vec::new();

        for line in output_str.lines() {
            if let Ok(time) = line.trim().parse::<f64>() {
                keyframes.push(time);
            }
        }

        keyframes.sort_by(|a, b| a.partial_cmp(b).unwrap());
        Ok(keyframes)
    }

    /// 寻找最近的关键帧
    fn find_nearest_keyframe(keyframes: &[f64], target_time: f64) -> f64 {
        if keyframes.is_empty() {
            return target_time;
        }

        let mut nearest = keyframes[0];
        let mut min_distance = (target_time - keyframes[0]).abs();

        for &keyframe in keyframes {
            let distance = (target_time - keyframe).abs();
            if distance < min_distance {
                min_distance = distance;
                nearest = keyframe;
            }
        }

        nearest
    }

    /// 获取视频缩略图
    pub fn generate_thumbnail(
        input_path: &str,
        output_path: &str,
        timestamp: f64,
        width: u32,
        height: u32,
    ) -> Result<()> {
        use tracing::{info, error};

        // 输入验证
        if !Path::new(input_path).exists() {
            return Err(anyhow!("输入文件不存在: {}", input_path));
        }

        // 确保输出目录存在
        if let Some(parent_dir) = Path::new(output_path).parent() {
            if !parent_dir.exists() {
                std::fs::create_dir_all(parent_dir)
                    .map_err(|e| anyhow!("创建输出目录失败: {}", e))?;
                info!("创建输出目录: {}", parent_dir.display());
            }
        }

        // 构建优化的FFmpeg命令参数
        let timestamp_str = timestamp.to_string();
        let scale_filter = format!("scale={}:{}:force_original_aspect_ratio=decrease", width, height);

        let args = [
            "-hide_banner",              // 隐藏版权信息
            "-loglevel", "error",        // 只显示错误信息
            "-i", input_path,
            "-ss", &timestamp_str,       // 寻找到指定时间戳
            "-vframes", "1",             // 只提取一帧
            "-vf", &scale_filter,        // 缩放过滤器，保持宽高比
            "-pix_fmt", "yuvj420p",      // 指定像素格式，兼容性好
            "-q:v", "2",                 // 设置高质量
            "-f", "image2",              // 指定输出格式
            "-update", "1",              // 更新模式，避免某些格式问题
            "-y",                        // 覆盖输出文件
            output_path
        ];

        info!(
            input_path = %input_path,
            output_path = %output_path,
            timestamp = timestamp,
            size = format!("{}x{}", width, height),
            command = format!("ffmpeg {}", args.join(" ")),
            "开始执行FFmpeg缩略图生成"
        );

        let output = Self::create_hidden_command("ffmpeg")
            .args(args)
            .output()
            .map_err(|e| anyhow!("执行缩略图生成失败: {}", e))?;

        let stderr_msg = String::from_utf8_lossy(&output.stderr);
        let stdout_msg = String::from_utf8_lossy(&output.stdout);

        if !output.status.success() {
            error!(
                input_path = %input_path,
                output_path = %output_path,
                timestamp = timestamp,
                size = format!("{}x{}", width, height),
                exit_code = ?output.status.code(),
                stderr = %stderr_msg,
                stdout = %stdout_msg,
                command = format!("ffmpeg {}", args.join(" ")),
                "FFmpeg缩略图生成命令失败"
            );

            return Err(anyhow!(
                "FFmpeg缩略图生成失败 (退出码: {:?}): {}",
                output.status.code(),
                if stderr_msg.trim().is_empty() { "未知错误" } else { &stderr_msg }
            ));
        }

        // 记录成功的FFmpeg输出（用于调试）
        if !stderr_msg.trim().is_empty() || !stdout_msg.trim().is_empty() {
            info!(
                input_path = %input_path,
                output_path = %output_path,
                stderr = %stderr_msg,
                stdout = %stdout_msg,
                "FFmpeg缩略图生成命令执行完成"
            );
        }

        // 验证输出文件是否存在
        if !Path::new(output_path).exists() {
            error!(
                input_path = %input_path,
                output_path = %output_path,
                timestamp = timestamp,
                stderr = %stderr_msg,
                stdout = %stdout_msg,
                "FFmpeg命令成功但输出文件不存在"
            );
            return Err(anyhow!("FFmpeg命令成功但输出文件不存在: {}", output_path));
        }

        // 验证输出文件大小
        let file_size = std::fs::metadata(output_path)
            .map_err(|e| anyhow!("获取输出文件信息失败: {}", e))?
            .len();

        if file_size == 0 {
            error!(
                input_path = %input_path,
                output_path = %output_path,
                "生成的缩略图文件大小为0"
            );
            return Err(anyhow!("生成的缩略图文件大小为0: {}", output_path));
        }

        info!(
            input_path = %input_path,
            output_path = %output_path,
            file_size = file_size,
            "缩略图生成成功"
        );

        Ok(())
    }

    /// 带重试机制的缩略图生成
    pub fn generate_thumbnail_with_retry(
        input_path: &str,
        output_path: &str,
        timestamp: f64,
        width: u32,
        height: u32,
    ) -> Result<()> {
        use tracing::{info, warn, error};

        // 获取视频时长用于计算备用时间戳
        let video_duration = match Self::get_video_info(input_path) {
            Ok(info) => info.duration,
            Err(e) => {
                warn!("无法获取视频时长，将使用默认重试策略: {}", e);
                60.0 // 默认假设60秒
            }
        };

        // 定义重试策略：原时间戳、0秒、中间时间点、1秒
        let retry_timestamps = vec![
            timestamp,                    // 原始时间戳
            0.0,                         // 视频开始
            video_duration / 2.0,        // 视频中间
            1.0,                         // 1秒处
            video_duration * 0.25,       // 25%处
            video_duration * 0.75,       // 75%处
        ];

        let mut last_error = None;

        for (attempt, &retry_timestamp) in retry_timestamps.iter().enumerate() {
            // 跳过超出视频长度的时间戳
            if retry_timestamp >= video_duration && video_duration > 0.0 {
                continue;
            }

            info!(
                attempt = attempt + 1,
                timestamp = retry_timestamp,
                input_path = %input_path,
                output_path = %output_path,
                "尝试生成缩略图"
            );

            match Self::generate_thumbnail(input_path, output_path, retry_timestamp, width, height) {
                Ok(_) => {
                    if attempt > 0 {
                        info!(
                            successful_attempt = attempt + 1,
                            successful_timestamp = retry_timestamp,
                            original_timestamp = timestamp,
                            "缩略图生成重试成功"
                        );
                    }
                    return Ok(());
                }
                Err(e) => {
                    warn!(
                        attempt = attempt + 1,
                        timestamp = retry_timestamp,
                        error = %e,
                        "缩略图生成尝试失败"
                    );
                    last_error = Some(e);

                    // 如果输出文件存在但有错误，删除它以便下次重试
                    if Path::new(output_path).exists() {
                        if let Err(remove_err) = std::fs::remove_file(output_path) {
                            warn!("删除失败的缩略图文件时出错: {}", remove_err);
                        }
                    }
                }
            }
        }

        // 所有重试都失败了
        error!(
            input_path = %input_path,
            output_path = %output_path,
            total_attempts = retry_timestamps.len(),
            "所有缩略图生成尝试都失败"
        );

        Err(last_error.unwrap_or_else(|| anyhow!("所有缩略图生成尝试都失败")))
    }

    /// 缩略图生成预检查
    pub fn validate_thumbnail_generation(
        input_path: &str,
        output_path: &str,
        timestamp: f64,
    ) -> Result<()> {
        use tracing::{info, warn};

        // 1. 检查输入文件
        if !Path::new(input_path).exists() {
            return Err(anyhow!("输入视频文件不存在: {}", input_path));
        }

        // 2. 检查输入文件是否可读
        if let Err(e) = std::fs::File::open(input_path) {
            return Err(anyhow!("无法读取输入视频文件: {}", e));
        }

        // 3. 获取视频信息验证文件有效性
        let video_info = Self::get_video_info(input_path)
            .map_err(|e| anyhow!("视频文件无效或损坏: {}", e))?;

        // 4. 验证时间戳合理性
        if timestamp < 0.0 {
            warn!("时间戳为负数，将调整为0: {}", timestamp);
        } else if video_info.duration > 0.0 && timestamp >= video_info.duration {
            warn!(
                "时间戳超出视频长度 ({}s >= {}s)，将使用重试机制",
                timestamp, video_info.duration
            );
        }

        // 5. 检查输出目录
        if let Some(parent_dir) = Path::new(output_path).parent() {
            if !parent_dir.exists() {
                info!("输出目录不存在，将创建: {}", parent_dir.display());
                std::fs::create_dir_all(parent_dir)
                    .map_err(|e| anyhow!("无法创建输出目录: {}", e))?;
            }

            // 检查目录是否可写
            let test_file = parent_dir.join(".thumbnail_test");
            if let Err(e) = std::fs::write(&test_file, b"test") {
                return Err(anyhow!("输出目录不可写: {}", e));
            }
            let _ = std::fs::remove_file(&test_file); // 清理测试文件
        }

        // 6. 检查是否有足够的磁盘空间（简单检查）
        if let Ok(metadata) = std::fs::metadata(input_path) {
            let file_size = metadata.len();
            // 假设缩略图大小不会超过原文件的1%
            let estimated_thumbnail_size = file_size / 100;

            // 这里可以添加更复杂的磁盘空间检查逻辑
            if estimated_thumbnail_size > 10 * 1024 * 1024 { // 10MB
                warn!("原视频文件很大，缩略图生成可能需要较长时间");
            }
        }

        info!(
            input_path = %input_path,
            output_path = %output_path,
            video_duration = video_info.duration,
            video_size = format!("{}x{}", video_info.width, video_info.height),
            "缩略图生成预检查通过"
        );

        Ok(())
    }

    /// 获取视频信息
    pub fn get_video_info(input_path: &str) -> Result<VideoMetadata> {
        if !Path::new(input_path).exists() {
            return Err(anyhow!("输入文件不存在: {}", input_path));
        }

        let output = Self::create_hidden_command("ffprobe")
            .args([
                "-v", "quiet",
                "-print_format", "json",
                "-show_format",
                "-show_streams",
                input_path
            ])
            .output()
            .map_err(|e| anyhow!("执行ffprobe失败: {}", e))?;

        if !output.status.success() {
            let error_msg = String::from_utf8_lossy(&output.stderr);
            return Err(anyhow!("ffprobe执行失败: {}", error_msg));
        }

        let json_str = String::from_utf8_lossy(&output.stdout);
        let json: Value = serde_json::from_str(&json_str)
            .map_err(|e| anyhow!("解析ffprobe输出失败: {}", e))?;

        // 查找视频流
        let streams = json["streams"].as_array()
            .ok_or_else(|| anyhow!("未找到流信息"))?;

        for stream in streams {
            if stream["codec_type"].as_str() == Some("video") {
                let width = stream["width"].as_u64().unwrap_or(0) as u32;
                let height = stream["height"].as_u64().unwrap_or(0) as u32;
                let duration = stream["duration"].as_str()
                    .and_then(|s| s.parse::<f64>().ok())
                    .unwrap_or(0.0);
                let fps = stream["r_frame_rate"].as_str()
                    .and_then(|s| {
                        let parts: Vec<&str> = s.split('/').collect();
                        if parts.len() == 2 {
                            let num = parts[0].parse::<f64>().ok()?;
                            let den = parts[1].parse::<f64>().ok()?;
                            if den != 0.0 { Some(num / den) } else { None }
                        } else {
                            s.parse::<f64>().ok()
                        }
                    })
                    .unwrap_or(0.0);

                // 查找音频流信息
                let mut has_audio = false;
                let mut audio_codec = None;
                let mut audio_bitrate = None;
                let mut audio_sample_rate = None;

                for audio_stream in streams {
                    if audio_stream["codec_type"].as_str() == Some("audio") {
                        has_audio = true;
                        audio_codec = audio_stream["codec_name"].as_str().map(|s| s.to_string());
                        audio_bitrate = audio_stream["bit_rate"].as_str()
                            .and_then(|s| s.parse::<u64>().ok());
                        audio_sample_rate = audio_stream["sample_rate"].as_str()
                            .and_then(|s| s.parse::<u32>().ok());
                        break;
                    }
                }

                return Ok(VideoMetadata {
                    width,
                    height,
                    duration,
                    fps,
                    codec: stream["codec_name"].as_str().unwrap_or("unknown").to_string(),
                    bitrate: stream["bit_rate"].as_str()
                        .and_then(|s| s.parse::<u64>().ok())
                        .unwrap_or(0),
                    format: json["format"]["format_name"].as_str().unwrap_or("unknown").to_string(),
                    has_audio,
                    audio_codec,
                    audio_bitrate,
                    audio_sample_rate,
                });
            }
        }

        Err(anyhow!("未找到视频流"))
    }

    /// 检查 FFmpeg 版本
    pub fn get_version() -> Result<String> {
        let output = Self::create_hidden_command("ffmpeg")
            .arg("-version")
            .output()
            .map_err(|e| anyhow!("获取 FFmpeg 版本失败: {}", e))?;

        if !output.status.success() {
            return Err(anyhow!("FFmpeg 不可用"));
        }

        let version_str = String::from_utf8_lossy(&output.stdout);
        let first_line = version_str.lines().next().unwrap_or("Unknown version");
        Ok(first_line.to_string())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use tempfile::TempDir;

    /// 创建测试用的临时视频文件（模拟）
    fn create_test_video_file(temp_dir: &TempDir, filename: &str) -> String {
        let video_path = temp_dir.path().join(filename);
        // 创建一个假的视频文件用于测试
        fs::write(&video_path, b"fake video content").unwrap();
        video_path.to_string_lossy().to_string()
    }

    /// 创建测试用的输出路径
    fn create_test_output_path(temp_dir: &TempDir, filename: &str) -> String {
        let output_path = temp_dir.path().join(filename);
        output_path.to_string_lossy().to_string()
    }

    #[test]
    fn test_validate_thumbnail_generation_missing_input() {
        let temp_dir = TempDir::new().unwrap();
        let non_existent_path = temp_dir.path().join("non_existent.mp4");
        let output_path = create_test_output_path(&temp_dir, "output.jpg");

        let result = FFmpegService::validate_thumbnail_generation(
            &non_existent_path.to_string_lossy(),
            &output_path,
            1.0,
        );

        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("输入视频文件不存在"));
    }

    #[test]
    fn test_validate_thumbnail_generation_output_directory_creation() {
        let temp_dir = TempDir::new().unwrap();
        let video_path = create_test_video_file(&temp_dir, "test.mp4");

        // 创建一个不存在的子目录路径
        let output_path = temp_dir.path()
            .join("subdir")
            .join("output.jpg")
            .to_string_lossy()
            .to_string();

        let result = FFmpegService::validate_thumbnail_generation(
            &video_path,
            &output_path,
            1.0,
        );

        // 应该会因为视频文件无效而失败
        assert!(result.is_err());
        // 但是在失败之前，目录创建逻辑应该已经执行
        // 由于我们的预检查在获取视频信息时就失败了，目录可能不会被创建
        // 所以我们测试一个更简单的场景
    }

    #[test]
    fn test_directory_creation_logic() {
        let temp_dir = TempDir::new().unwrap();
        let subdir_path = temp_dir.path().join("test_subdir");
        let output_path = subdir_path.join("output.jpg");

        // 确保目录不存在
        assert!(!subdir_path.exists());

        // 手动测试目录创建逻辑
        if let Some(parent_dir) = output_path.parent() {
            if !parent_dir.exists() {
                std::fs::create_dir_all(parent_dir).unwrap();
            }
        }

        // 验证目录被创建
        assert!(subdir_path.exists());
    }

    #[test]
    fn test_ffmpeg_command_args_construction() {
        // 测试FFmpeg命令参数的构造是否正确
        let input_path = "/path/to/video.mp4";
        let output_path = "/path/to/output.jpg";
        let timestamp = 5.5;
        let width = 160;
        let height = 120;

        let result = FFmpegService::generate_thumbnail(
            input_path,
            output_path,
            timestamp,
            width,
            height,
        );

        // 应该失败，因为文件不存在
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("输入文件不存在"));
    }
}
