use tauri::{command, State, Emitter};
use std::sync::Arc;
use crate::app_state::AppState;
use crate::business::services::material_service::MaterialService;
use crate::data::repositories::material_repository::MaterialRepository;
use crate::infrastructure::ffmpeg::FFmpegService;
use crate::data::models::material::{
    CreateMaterialRequest, MaterialImportResult, MaterialProcessingConfig,
    Material, MaterialStats, ProcessingStatus, MaterialMetadata
};


/// 导入素材命令
/// 遵循 Tauri 开发规范的命令设计模式
#[command]
pub async fn import_materials(
    state: State<'_, AppState>,
    request: CreateMaterialRequest,
) -> Result<MaterialImportResult, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;
    
    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    let mut config = MaterialProcessingConfig::default();
    config.auto_process = Some(request.auto_process);
    if let Some(max_duration) = request.max_segment_duration {
        config.max_segment_duration = max_duration;
    }
    if let Some(skip_ms) = request.skip_start_ms {
        config.skip_start_ms = Some(skip_ms);
    }

    MaterialService::import_materials(repository, request, &config)
        .map_err(|e| e.to_string())
}

/// 异步导入素材命令
/// 遵循 Tauri 开发规范的异步命令设计模式
/// 提供实时进度反馈和非阻塞用户体验
#[command]
pub async fn import_materials_async(
    state: State<'_, AppState>,
    request: CreateMaterialRequest,
    app_handle: tauri::AppHandle,
) -> Result<MaterialImportResult, String> {
    // 添加调试日志
    println!("Import request received with model_id: {:?}", request.model_id);
    // 获取数据库实例
    let database = state.get_database();

    let mut config = MaterialProcessingConfig::default();
    config.auto_process = Some(request.auto_process);
    if let Some(max_duration) = request.max_segment_duration {
        config.max_segment_duration = max_duration;
    }

    // 创建一个新的MaterialRepository实例用于异步操作
    let async_repository = MaterialRepository::new(database)
        .map_err(|e| format!("创建异步仓库失败: {}", e))?;
    let repository_arc = Arc::new(async_repository);

    // 使用简化的异步导入，直接通过 Tauri 事件发送进度
    import_materials_with_tauri_events(
        repository_arc,
        request,
        config,
        app_handle,
    ).await.map_err(|e| e.to_string())
}

/// 使用 Tauri 事件系统的异步导入实现
async fn import_materials_with_tauri_events(
    repository: Arc<MaterialRepository>,
    request: CreateMaterialRequest,
    config: MaterialProcessingConfig,
    app_handle: tauri::AppHandle,
) -> Result<MaterialImportResult, anyhow::Error> {
    use std::path::Path;
    use std::time::Instant;
    use tracing::{info, warn, error, debug};


    let start_time = Instant::now();

    info!(
        project_id = %request.project_id,
        file_count = request.file_paths.len(),
        "开始异步导入素材文件"
    );

    let mut result = MaterialImportResult {
        total_files: request.file_paths.len() as u32,
        processed_files: 0,
        skipped_files: 0,
        failed_files: 0,
        created_materials: Vec::new(),
        errors: Vec::new(),
        processing_time: 0.0,
    };

    // 发布开始导入事件
    let _ = app_handle.emit("material_import_progress", serde_json::json!({
        "current_file": "准备导入...",
        "processed_count": 0,
        "total_count": result.total_files,
        "current_status": "正在准备导入文件",
        "progress_percentage": 0.0
    }));

    // 添加小延迟确保用户能看到loading动画
    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;

    // 异步处理每个文件
    for (index, file_path) in request.file_paths.iter().enumerate() {
        debug!(file_path = %file_path, "异步处理文件");

        // 发布当前文件处理进度
        let _ = app_handle.emit("material_import_progress", serde_json::json!({
            "current_file": file_path,
            "processed_count": index,
            "total_count": result.total_files,
            "current_status": format!("正在处理文件: {}", Path::new(file_path).file_name()
                .and_then(|n| n.to_str()).unwrap_or("unknown")),
            "progress_percentage": (index as f64 / result.total_files as f64) * 100.0
        }));

        // 处理单个文件 - 使用完整的业务逻辑
        match process_single_file_with_full_logic(
            Arc::clone(&repository),
            &request.project_id,
            file_path,
            &config,
            request.model_id.clone(),
            app_handle.clone(),
        ).await {
            Ok(Some(material)) => {
                info!(
                    file_path = %file_path,
                    material_id = %material.id,
                    "文件异步处理成功"
                );
                result.created_materials.push(material);
                result.processed_files += 1;
            }
            Ok(None) => {
                // 文件被跳过（重复）
                warn!(file_path = %file_path, "文件被跳过（重复）");
                result.skipped_files += 1;
            }
            Err(e) => {
                error!(
                    file_path = %file_path,
                    error = %e,
                    "文件异步处理失败"
                );
                result.failed_files += 1;
                result.errors.push(format!("处理文件 {} 失败: {}", file_path, e));
            }
        }

        // 让出控制权，避免长时间占用线程
        tokio::task::yield_now().await;
    }

    result.processing_time = start_time.elapsed().as_secs_f64();

    let success = result.failed_files == 0;

    info!(
        processed_files = result.processed_files,
        skipped_files = result.skipped_files,
        failed_files = result.failed_files,
        processing_time = result.processing_time,
        "异步素材导入完成"
    );

    // 发布导入完成事件
    if success {
        let _ = app_handle.emit("material_import_completed", &result);
    } else {
        let _ = app_handle.emit("material_import_failed",
            format!("导入失败，{} 个文件处理失败", result.failed_files));
    }

    Ok(result)
}

/// 使用完整业务逻辑的单文件处理函数
async fn process_single_file_with_full_logic(
    repository: Arc<MaterialRepository>,
    project_id: &str,
    file_path: &str,
    config: &MaterialProcessingConfig,
    model_id: Option<String>,
    app_handle: tauri::AppHandle,
) -> Result<Option<Material>, anyhow::Error> {
    use anyhow::anyhow;
    use std::path::Path;
    use std::fs;
    use tokio::task;
    use crate::data::models::material::{Material, MaterialType, ProcessingStatus};
    use crate::business::errors::error_utils;
    use tracing::{info, warn, error, debug};

    debug!(file_path = %file_path, "开始处理单个文件");

    // 验证文件路径
    error_utils::validate_file_path(file_path)
        .map_err(|e| anyhow!("文件验证失败: {}", e))?;

    let path = Path::new(file_path);

    // 获取文件信息
    let metadata = fs::metadata(path)?;
    let file_size = metadata.len();

    // 计算MD5哈希（在独立任务中执行）
    let file_path_clone = file_path.to_string();
    let md5_hash = task::spawn_blocking(move || {
        MaterialService::calculate_md5(&file_path_clone)
    }).await??;

    // 检查是否已存在相同的文件
    if repository.exists_by_md5(project_id, &md5_hash)? {
        warn!(file_path = %file_path, "文件已存在，跳过");
        return Ok(None); // 跳过重复文件
    }

    // 获取文件名和扩展名
    let file_name = path.file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("unknown")
        .to_string();

    let extension = path.extension()
        .and_then(|ext| ext.to_str())
        .unwrap_or("");

    // 确定素材类型
    let material_type = MaterialType::from_extension(extension);

    // 添加调试日志
    println!("Creating material with model_id: {:?}", model_id);

    // 创建素材对象（带模特绑定）
    let mut material = Material::new_with_model(
        project_id.to_string(),
        file_name.clone(),
        file_path.to_string(),
        file_size,
        md5_hash,
        material_type.clone(),
        model_id,
    );

    // 保存到数据库
    repository.create(&material)?;
    info!(material_id = %material.id, file_name = %file_name, "素材创建成功");

    // 发送处理进度事件
    let _ = app_handle.emit("material_processing_progress", serde_json::json!({
        "material_id": material.id,
        "file_name": file_name,
        "stage": "开始处理",
        "progress_percentage": 0.0
    }));

    // 如果启用自动处理，则进行完整的业务处理
    if config.auto_process.unwrap_or(true) {
        // 更新状态为处理中
        MaterialService::update_material_status(
            &repository,
            &material.id,
            ProcessingStatus::Processing,
            None,
        )?;

        // 1. 提取元数据
        let _ = app_handle.emit("material_processing_progress", serde_json::json!({
            "material_id": material.id,
            "file_name": file_name,
            "stage": "提取元数据",
            "progress_percentage": 25.0
        }));

        let original_path = material.original_path.clone();
        let material_type_clone = material.material_type.clone();

        match task::spawn_blocking(move || {
            MaterialService::extract_metadata(&original_path, &material_type_clone)
        }).await? {
            Ok(metadata) => {
                material.set_metadata(metadata);
                repository.update(&material)?;
                info!(material_id = %material.id, "元数据提取成功");
            }
            Err(e) => {
                error!(material_id = %material.id, error = %e, "元数据提取失败");
                MaterialService::update_material_status(
                    &repository,
                    &material.id,
                    ProcessingStatus::Failed,
                    Some(format!("元数据提取失败: {}", e)),
                )?;
                return Err(e);
            }
        }

        // 2. 场景检测（如果是视频且启用了场景检测）
        if matches!(material.material_type, MaterialType::Video) && config.enable_scene_detection {
            let _ = app_handle.emit("material_processing_progress", serde_json::json!({
                "material_id": material.id,
                "file_name": file_name,
                "stage": "场景检测",
                "progress_percentage": 50.0
            }));

            let original_path = material.original_path.clone();
            let threshold = config.scene_detection_threshold;

            match task::spawn_blocking(move || {
                MaterialService::detect_video_scenes(&original_path, threshold)
            }).await? {
                Ok(scene_detection) => {
                    info!(material_id = %material.id, scenes = scene_detection.scenes.len(), "场景检测成功");
                    material.set_scene_detection(scene_detection);
                    repository.update(&material)?;
                }
                Err(e) => {
                    // 场景检测失败不应该导致整个处理失败
                    warn!(material_id = %material.id, error = %e, "场景检测失败");
                }
            }
        }

        // 3. 检查是否需要切分视频
        let should_segment = material.needs_segmentation(config.max_segment_duration) ||
                           (matches!(material.material_type, MaterialType::Video) && material.scene_detection.is_some());

        if should_segment {
            let _ = app_handle.emit("material_processing_progress", serde_json::json!({
                "material_id": material.id,
                "file_name": file_name,
                "stage": "视频切分",
                "progress_percentage": 75.0
            }));

            let repository_clone = Arc::clone(&repository);
            let material_clone = material.clone();
            let config_clone = config.clone();

            match task::spawn_blocking(move || {
                MaterialService::segment_video(&repository_clone, &material_clone, &config_clone)
            }).await? {
                Ok(_) => {
                    info!(material_id = %material.id, "视频切分完成");
                }
                Err(e) => {
                    error!(material_id = %material.id, error = %e, "视频切分失败");
                    MaterialService::update_material_status(
                        &repository,
                        &material.id,
                        ProcessingStatus::Failed,
                        Some(format!("视频切分失败: {}", e)),
                    )?;
                    return Err(e);
                }
            }
        }

        // 标记为完成
        MaterialService::update_material_status(
            &repository,
            &material.id,
            ProcessingStatus::Completed,
            None,
        )?;

        // 发送处理完成事件
        let _ = app_handle.emit("material_processing_progress", serde_json::json!({
            "material_id": material.id,
            "file_name": file_name,
            "stage": "处理完成",
            "progress_percentage": 100.0
        }));

        info!(material_id = %material.id, "素材处理完成");
    }

    Ok(Some(material))
}

/// 选择文件夹进行批量导入
#[command]
pub async fn select_material_folders(app_handle: tauri::AppHandle) -> Result<Vec<String>, String> {
    use crate::presentation::commands::system_commands::select_directory;

    // 使用现有的目录选择功能，但允许多选
    // 注意：这是一个简化实现，实际可能需要修改系统命令以支持多选
    match select_directory(app_handle) {
        Ok(Some(folder_path)) => Ok(vec![folder_path]),
        Ok(None) => Ok(Vec::new()),
        Err(e) => Err(e),
    }
}

/// 扫描文件夹中的素材文件
#[command]
pub async fn scan_folder_materials(
    folder_paths: Vec<String>,
    recursive: bool,
    file_types: Vec<String>, // 文件类型过滤，如 ["mp4", "avi", "mov"]
) -> Result<Vec<String>, String> {
    use std::path::Path;

    let mut all_files = Vec::new();

    for folder_path in folder_paths {
        let folder = Path::new(&folder_path);
        if !folder.exists() || !folder.is_dir() {
            continue;
        }

        let files = if recursive {
            scan_directory_recursive(folder, &file_types)?
        } else {
            scan_directory_shallow(folder, &file_types)?
        };

        all_files.extend(files);
    }

    // 去重
    all_files.sort();
    all_files.dedup();

    Ok(all_files)
}

/// 递归扫描目录
fn scan_directory_recursive(dir: &std::path::Path, file_types: &[String]) -> Result<Vec<String>, String> {
    use std::fs;

    let mut files = Vec::new();

    let entries = fs::read_dir(dir)
        .map_err(|e| format!("读取目录失败 {}: {}", dir.display(), e))?;

    for entry in entries {
        let entry = entry.map_err(|e| format!("读取目录项失败: {}", e))?;
        let path = entry.path();

        if path.is_dir() {
            // 递归扫描子目录
            let sub_files = scan_directory_recursive(&path, file_types)?;
            files.extend(sub_files);
        } else if path.is_file() {
            // 检查文件类型
            if let Some(extension) = path.extension() {
                if let Some(ext_str) = extension.to_str() {
                    let ext_lower = ext_str.to_lowercase();
                    if file_types.is_empty() || file_types.contains(&ext_lower) {
                        files.push(path.to_string_lossy().to_string());
                    }
                }
            }
        }
    }

    Ok(files)
}

/// 浅层扫描目录（不递归）
fn scan_directory_shallow(dir: &std::path::Path, file_types: &[String]) -> Result<Vec<String>, String> {
    use std::fs;

    let mut files = Vec::new();

    let entries = fs::read_dir(dir)
        .map_err(|e| format!("读取目录失败 {}: {}", dir.display(), e))?;

    for entry in entries {
        let entry = entry.map_err(|e| format!("读取目录项失败: {}", e))?;
        let path = entry.path();

        if path.is_file() {
            // 检查文件类型
            if let Some(extension) = path.extension() {
                if let Some(ext_str) = extension.to_str() {
                    let ext_lower = ext_str.to_lowercase();
                    if file_types.is_empty() || file_types.contains(&ext_lower) {
                        files.push(path.to_string_lossy().to_string());
                    }
                }
            }
        }
    }

    Ok(files)
}

/// 获取项目素材列表命令
#[command]
pub async fn get_project_materials(
    state: State<'_, AppState>,
    project_id: String,
) -> Result<Vec<Material>, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::get_project_materials(repository, &project_id)
        .map_err(|e| e.to_string())
}

/// 获取所有素材列表命令
#[command]
pub async fn get_all_materials(
    state: State<'_, AppState>,
) -> Result<Vec<Material>, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::get_all_materials(repository)
        .map_err(|e| e.to_string())
}

/// 获取素材详情命令
#[command]
pub async fn get_material_by_id(
    state: State<'_, AppState>,
    id: String,
) -> Result<Option<Material>, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;
    
    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::get_material_by_id(repository, &id)
        .map_err(|e| e.to_string())
}

/// 删除素材命令
#[command]
pub async fn delete_material(
    state: State<'_, AppState>,
    id: String,
) -> Result<(), String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::delete_material(repository, &id)
        .map_err(|e| e.to_string())
}

/// 批量删除素材命令
#[command]
pub async fn batch_delete_materials(
    state: State<'_, AppState>,
    material_ids: Vec<String>,
) -> Result<crate::data::models::material::BatchDeleteResult, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::batch_delete_materials(repository, material_ids)
        .map_err(|e| e.to_string())
}

/// 获取项目素材统计命令
#[command]
pub async fn get_project_material_stats(
    state: State<'_, AppState>,
    project_id: String,
) -> Result<MaterialStats, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;
    
    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::get_project_stats(repository, &project_id)
        .map_err(|e| e.to_string())
}

/// 批量处理素材命令
#[command]
pub async fn batch_process_materials(
    state: State<'_, AppState>,
    material_ids: Vec<String>,
) -> Result<Vec<String>, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;
    
    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    let config = MaterialProcessingConfig::default();
    
    MaterialService::batch_process_materials(repository, material_ids, &config)
        .map_err(|e| e.to_string())
}

/// 更新素材处理状态命令
#[command]
pub async fn update_material_status(
    state: State<'_, AppState>,
    id: String,
    status: String,
    error_message: Option<String>,
) -> Result<(), String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;
    
    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    // 解析状态字符串
    let processing_status = match status.as_str() {
        "Pending" => ProcessingStatus::Pending,
        "Processing" => ProcessingStatus::Processing,
        "Completed" => ProcessingStatus::Completed,
        "Failed" => ProcessingStatus::Failed,
        "Skipped" => ProcessingStatus::Skipped,
        _ => return Err("无效的处理状态".to_string()),
    };

    MaterialService::update_material_status(repository, &id, processing_status, error_message)
        .map_err(|e| e.to_string())
}

/// 清理失效素材命令
#[command]
pub async fn cleanup_invalid_materials(
    state: State<'_, AppState>,
    project_id: String,
) -> Result<String, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;
    
    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    let cleaned_count = MaterialService::cleanup_invalid_materials(repository, &project_id)
        .map_err(|e| e.to_string())?;

    Ok(format!("已清理 {} 个失效的素材记录", cleaned_count))
}

/// 检查文件是否为支持格式命令
#[command]
pub async fn is_supported_format(file_path: String) -> Result<bool, String> {
    Ok(MaterialService::is_supported_format(&file_path))
}

/// 获取支持的文件扩展名命令
#[command]
pub async fn get_supported_extensions() -> Result<Vec<String>, String> {
    let extensions = MaterialService::get_supported_extensions()
        .into_iter()
        .map(|s| s.to_string())
        .collect();
    Ok(extensions)
}

/// 选择素材文件命令
#[command]
pub async fn select_material_files(app: tauri::AppHandle) -> Result<Vec<String>, String> {
    use tauri_plugin_dialog::DialogExt;

    let dialog = app.dialog().file()
        .set_title("选择素材文件")
        .add_filter("视频文件", &["mp4", "avi", "mov", "mkv", "wmv", "flv", "webm", "m4v"])
        .add_filter("音频文件", &["mp3", "wav", "flac", "aac", "ogg", "wma", "m4a"])
        .add_filter("图片文件", &["jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp", "svg"])
        .add_filter("文档文件", &["pdf", "doc", "docx", "txt", "md", "rtf"])
        .add_filter("所有支持的文件", &MaterialService::get_supported_extensions());

    let (tx, rx) = std::sync::mpsc::channel();

    dialog.pick_files(move |file_paths| {
        let paths = file_paths.map(|paths| {
            paths.into_iter()
                .map(|path| path.to_string())
                .collect::<Vec<String>>()
        }).unwrap_or_default();
        let _ = tx.send(paths);
    });

    match rx.recv_timeout(std::time::Duration::from_secs(30)) {
        Ok(paths) => Ok(paths),
        Err(_) => Err("文件选择操作超时或失败".to_string()),
    }
}

/// 验证素材文件命令
#[command]
pub async fn validate_material_files(file_paths: Vec<String>) -> Result<Vec<String>, String> {
    let mut valid_files = Vec::new();
    
    for file_path in file_paths {
        if std::path::Path::new(&file_path).exists() && 
           MaterialService::is_supported_format(&file_path) {
            valid_files.push(file_path);
        }
    }
    
    Ok(valid_files)
}

/// 获取文件信息命令
#[command]
pub async fn get_file_info(file_path: String) -> Result<serde_json::Value, String> {
    use std::path::Path;
    
    let path = Path::new(&file_path);
    
    if !path.exists() {
        return Err("文件不存在".to_string());
    }
    
    let metadata = std::fs::metadata(path)
        .map_err(|e| format!("获取文件信息失败: {}", e))?;
    
    let file_name = path.file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("unknown");
    
    let extension = path.extension()
        .and_then(|ext| ext.to_str())
        .unwrap_or("");
    
    let material_type = crate::data::models::material::MaterialType::from_extension(extension);
    
    let info = serde_json::json!({
        "name": file_name,
        "path": file_path,
        "size": metadata.len(),
        "extension": extension,
        "material_type": material_type,
        "is_supported": MaterialService::is_supported_format(&file_path),
        "modified": metadata.modified()
            .map(|time| time.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs())
            .unwrap_or(0)
    });
    
    Ok(info)
}

/// 检查 FFmpeg 是否可用命令
#[command]
pub async fn check_ffmpeg_available() -> Result<bool, String> {
    Ok(FFmpegService::is_available())
}

/// 获取 FFmpeg 版本命令
#[command]
pub async fn get_ffmpeg_version() -> Result<String, String> {
    FFmpegService::get_version().map_err(|e| e.to_string())
}

/// 获取 FFmpeg 状态信息命令
#[command]
pub async fn get_ffmpeg_status() -> Result<String, String> {
    FFmpegService::get_status_info().map_err(|e| e.to_string())
}

/// 提取文件元数据命令
#[command]
pub async fn extract_file_metadata(file_path: String) -> Result<MaterialMetadata, String> {
    FFmpegService::extract_metadata(&file_path).map_err(|e| e.to_string())
}

/// 检测视频场景命令
#[command]
pub async fn detect_video_scenes(
    file_path: String,
    threshold: f64,
) -> Result<Vec<f64>, String> {
    FFmpegService::detect_scenes(&file_path, threshold).map_err(|e| e.to_string())
}

/// 生成视频缩略图命令
#[command]
pub async fn generate_video_thumbnail(
    input_path: String,
    output_path: String,
    timestamp: f64,
    width: u32,
    height: u32,
) -> Result<(), String> {
    FFmpegService::generate_thumbnail(&input_path, &output_path, timestamp, width, height)
        .map_err(|e| e.to_string())
}

/// 为片段生成并保存缩略图
#[command]
pub async fn generate_and_save_segment_thumbnail(
    segment_id: String,
    app_state: State<'_, AppState>,
) -> Result<Option<String>, String> {
    use crate::infrastructure::ffmpeg::FFmpegService;
    use std::path::Path;

    // 获取片段信息（不跨越await点持有锁）
    let segment = {
        let material_repository_guard = app_state.material_repository.lock().unwrap();
        let material_repository = material_repository_guard.as_ref()
            .ok_or("MaterialRepository未初始化")?;

        // 这里不能使用await，因为会跨越锁的生命周期
        // 我们需要使用同步方法或者重新设计
        material_repository.get_segment_by_id_sync(&segment_id)
            .map_err(|e| e.to_string())?
    };

    let segment = match segment {
        Some(s) => s,
        None => return Err("片段不存在".to_string()),
    };

    // 如果已经有缩略图，直接返回
    if let Some(ref thumbnail_path) = segment.thumbnail_path {
        if std::path::Path::new(thumbnail_path).exists() {
            return Ok(Some(thumbnail_path.clone()));
        }
    }

    // 生成缩略图路径
    let video_path = &segment.file_path;
    let thumbnail_filename = format!("{}_thumbnail.jpg", segment.id);
    let video_dir = Path::new(video_path).parent()
        .ok_or("无法获取视频文件目录")?;
    let thumbnail_path = video_dir.join(thumbnail_filename);
    let thumbnail_path_str = thumbnail_path.to_string_lossy().to_string();

    // 生成缩略图（使用首帧）
    let timestamp = segment.start_time;

    // 获取视频元数据来确定合适的缩略图尺寸
    let video_info = FFmpegService::get_video_info(video_path)
        .map_err(|e| e.to_string())?;

    // 计算缩略图尺寸，保持宽高比，最大宽度160
    let max_width = 160;
    let (thumb_width, thumb_height) = if video_info.width > 0 && video_info.height > 0 {
        let aspect_ratio = video_info.width as f64 / video_info.height as f64;
        if video_info.width > max_width {
            let new_width = max_width;
            let new_height = (new_width as f64 / aspect_ratio).round() as u32;
            (new_width, new_height)
        } else {
            (video_info.width, video_info.height)
        }
    } else {
        // 如果无法获取视频尺寸，使用默认值
        (160, 120)
    };

    // 先进行预检查
    FFmpegService::validate_thumbnail_generation(
        video_path,
        &thumbnail_path_str,
        timestamp
    ).map_err(|e| e.to_string())?;

    // 使用带重试机制的缩略图生成
    FFmpegService::generate_thumbnail_with_retry(
        video_path,
        &thumbnail_path_str,
        timestamp,
        thumb_width,
        thumb_height
    ).map_err(|e| e.to_string())?;

    // 保存缩略图路径到数据库
    {
        let material_repository_guard = app_state.material_repository.lock().unwrap();
        let material_repository = material_repository_guard.as_ref()
            .ok_or("MaterialRepository未初始化")?;

        material_repository.update_segment_thumbnail(&segment_id, &thumbnail_path_str)
            .map_err(|e| e.to_string())?;
    }

    Ok(Some(thumbnail_path_str))
}

/// 读取缩略图文件并转换为base64数据URL
#[command]
pub async fn read_thumbnail_as_data_url(file_path: String) -> Result<String, String> {
    use std::fs;
    use base64::{Engine as _, engine::general_purpose};

    // 去掉Windows长路径前缀
    let clean_path = if file_path.starts_with("\\\\?\\") {
        &file_path[4..]
    } else {
        &file_path
    };

    // 读取文件
    let file_data = fs::read(clean_path)
        .map_err(|e| format!("读取文件失败: {}", e))?;

    // 转换为base64
    let base64_data = general_purpose::STANDARD.encode(&file_data);

    // 返回data URL
    Ok(format!("data:image/jpeg;base64,{}", base64_data))
}

/// 根据materialId获取缩略图base64数据URL
#[command]
pub async fn get_material_thumbnail_base64(
    material_id: String,
    app_state: State<'_, AppState>,
) -> Result<String, String> {
    use crate::infrastructure::ffmpeg::FFmpegService;
    use std::path::Path;
    use std::fs;
    use base64::{Engine as _, engine::general_purpose};

    // 获取素材信息
    let material = {
        let material_repository_guard = app_state.material_repository.lock().unwrap();
        let material_repository = material_repository_guard.as_ref()
            .ok_or("MaterialRepository未初始化")?;

        material_repository.get_by_id(&material_id)
            .map_err(|e| e.to_string())?
    };

    let material = match material {
        Some(m) => m,
        None => return Err("素材不存在".to_string()),
    };

    // 检查数据库中是否已有缩略图路径
    if let Some(ref thumbnail_path) = material.thumbnail_path {
        // 去掉Windows长路径前缀
        let clean_path = if thumbnail_path.starts_with("\\\\?\\") {
            &thumbnail_path[4..]
        } else {
            thumbnail_path
        };

        // 检查文件是否存在
        if Path::new(clean_path).exists() {
            // 文件存在，直接读取并返回
            let file_data = fs::read(clean_path)
                .map_err(|e| format!("读取缩略图文件失败: {}", e))?;

            let base64_data = general_purpose::STANDARD.encode(&file_data);
            return Ok(format!("data:image/jpeg;base64,{}", base64_data));
        }
    }

    // 根据素材类型处理
    match material.material_type {
        crate::data::models::material::MaterialType::Image => {
            // 图片类型：直接读取图片文件并返回base64
            let image_path = &material.original_path;

            // 去掉Windows长路径前缀
            let clean_image_path = if image_path.starts_with("\\\\?\\") {
                &image_path[4..]
            } else {
                image_path
            };

            // 检查图片文件是否存在
            if !Path::new(clean_image_path).exists() {
                let error_msg = format!("图片文件不存在: {}", clean_image_path);
                tracing::error!(
                    material_id = %material_id,
                    image_path = %clean_image_path,
                    "图片文件不存在"
                );
                return Err(error_msg);
            }

            // 直接读取图片文件
            let file_data = fs::read(clean_image_path)
                .map_err(|e| format!("读取图片文件失败: {}", e))?;

            // 根据文件扩展名确定MIME类型
            let mime_type = match Path::new(clean_image_path)
                .extension()
                .and_then(|ext| ext.to_str())
                .map(|ext| ext.to_lowercase())
                .as_deref()
            {
                Some("jpg") | Some("jpeg") => "image/jpeg",
                Some("png") => "image/png",
                Some("gif") => "image/gif",
                Some("webp") => "image/webp",
                Some("bmp") => "image/bmp",
                Some("tiff") | Some("tif") => "image/tiff",
                Some("svg") => "image/svg+xml",
                _ => "image/jpeg", // 默认为JPEG
            };

            let base64_data = general_purpose::STANDARD.encode(&file_data);
            return Ok(format!("data:{};base64,{}", mime_type, base64_data));
        }
        crate::data::models::material::MaterialType::Video => {
            // 视频类型：生成缩略图
            let video_path = &material.original_path;

            // 去掉Windows长路径前缀
            let clean_video_path = if video_path.starts_with("\\\\?\\") {
                &video_path[4..]
            } else {
                video_path
            };

            // 检查视频文件是否存在，不存在则报错
            if !Path::new(clean_video_path).exists() {
                let error_msg = format!("视频文件不存在，无法生成缩略图: {}", clean_video_path);
                tracing::error!(
                    material_id = %material_id,
                    video_path = %clean_video_path,
                    "视频文件不存在"
                );
                return Err(error_msg);
            }

    // 生成缩略图路径
    let thumbnail_filename = format!("{}_material_thumbnail.jpg", material.id);
    let video_dir = Path::new(clean_video_path).parent()
        .ok_or("无法获取视频文件目录")?;
    let thumbnail_path = video_dir.join(thumbnail_filename);
    let thumbnail_path_str = thumbnail_path.to_string_lossy().to_string();

    // 获取视频信息来确定合适的缩略图尺寸
    let video_info = FFmpegService::get_video_info(clean_video_path)
        .map_err(|e| e.to_string())?;

    // 计算缩略图尺寸，保持宽高比，最大宽度160
    let max_width = 160;
    let (thumb_width, thumb_height) = if video_info.width > 0 && video_info.height > 0 {
        let aspect_ratio = video_info.width as f64 / video_info.height as f64;
        if video_info.width > max_width {
            let new_width = max_width;
            let new_height = (new_width as f64 / aspect_ratio).round() as u32;
            (new_width, new_height)
        } else {
            (video_info.width, video_info.height)
        }
    } else {
        // 如果无法获取视频尺寸，使用默认值
        (160, 120)
    };

    // 生成缩略图（使用首帧，带重试机制）
    let timestamp = 0.0; // 使用视频开始时间

    // 先进行预检查
    if let Err(e) = FFmpegService::validate_thumbnail_generation(
        clean_video_path,
        &thumbnail_path_str,
        timestamp
    ) {
        let error_msg = format!(
            "缩略图生成预检查失败: {} (视频: {})",
            e, clean_video_path
        );
        tracing::error!(
            material_id = %material_id,
            video_path = %clean_video_path,
            error = %e,
            "缩略图生成预检查失败"
        );
        return Err(error_msg);
    }

    // 使用带重试机制的缩略图生成
    if let Err(e) = FFmpegService::generate_thumbnail_with_retry(
        clean_video_path,
        &thumbnail_path_str,
        timestamp,
        thumb_width,
        thumb_height
    ) {
        let error_msg = format!(
            "FFmpeg生成缩略图失败（已重试）: {} (视频: {}, 输出: {}, 时间戳: {}s)",
            e, clean_video_path, thumbnail_path_str, timestamp
        );
        tracing::error!(
            material_id = %material_id,
            video_path = %clean_video_path,
            thumbnail_path = %thumbnail_path_str,
            timestamp = timestamp,
            error = %e,
            "FFmpeg缩略图生成失败（已重试）"
        );
        return Err(error_msg);
    }

    // 验证缩略图文件是否生成成功
    if !Path::new(&thumbnail_path_str).exists() {
        let error_msg = format!(
            "缩略图文件生成失败，文件不存在: {}",
            thumbnail_path_str
        );
        tracing::error!(
            material_id = %material_id,
            thumbnail_path = %thumbnail_path_str,
            "缩略图文件生成失败"
        );
        return Err(error_msg);
    }

    // 保存缩略图路径到数据库
    {
        let material_repository_guard = app_state.material_repository.lock().unwrap();
        let material_repository = material_repository_guard.as_ref()
            .ok_or("MaterialRepository未初始化")?;

        material_repository.update_material_thumbnail(&material_id, &thumbnail_path_str)
            .map_err(|e| e.to_string())?;
    }

            // 读取生成的缩略图文件并返回base64
            let file_data = fs::read(&thumbnail_path_str)
                .map_err(|e| format!("读取生成的缩略图失败: {}", e))?;

            let base64_data = general_purpose::STANDARD.encode(&file_data);
            Ok(format!("data:image/jpeg;base64,{}", base64_data))
        }
        _ => {
            // 其他类型（音频等）不支持缩略图
            Err(format!("不支持的素材类型: {:?}", material.material_type))
        }
    }
}

/// 根据segmentId获取缩略图base64数据URL
#[command]
pub async fn get_segment_thumbnail_base64(
    segment_id: String,
    app_state: State<'_, AppState>,
) -> Result<String, String> {
    use crate::infrastructure::ffmpeg::FFmpegService;
    use std::path::Path;
    use std::fs;
    use base64::{Engine as _, engine::general_purpose};

    // 获取片段信息
    let segment = {
        let material_repository_guard = app_state.material_repository.lock().unwrap();
        let material_repository = material_repository_guard.as_ref()
            .ok_or("MaterialRepository未初始化")?;

        material_repository.get_segment_by_id_sync(&segment_id)
            .map_err(|e| e.to_string())?
    };

    let segment = match segment {
        Some(s) => s,
        None => return Err("片段不存在".to_string()),
    };

    // 检查数据库中是否已有缩略图路径
    if let Some(ref thumbnail_path) = segment.thumbnail_path {
        // 去掉Windows长路径前缀
        let clean_path = if thumbnail_path.starts_with("\\\\?\\") {
            &thumbnail_path[4..]
        } else {
            thumbnail_path
        };

        // 检查文件是否存在
        if Path::new(clean_path).exists() {
            // 文件存在，直接读取并返回
            let file_data = fs::read(clean_path)
                .map_err(|e| format!("读取缩略图文件失败: {}", e))?;

            let base64_data = general_purpose::STANDARD.encode(&file_data);
            return Ok(format!("data:image/jpeg;base64,{}", base64_data));
        }
    }

    // 缩略图不存在或文件已丢失，需要重新生成
    let video_path = &segment.file_path;

    // 去掉Windows长路径前缀
    let clean_video_path = if video_path.starts_with("\\\\?\\") {
        &video_path[4..]
    } else {
        video_path
    };

    // 检查视频文件是否存在，不存在则报错
    if !Path::new(clean_video_path).exists() {
        let error_msg = format!("视频文件不存在，无法生成缩略图: {}", clean_video_path);
        tracing::error!(
            segment_id = %segment_id,
            video_path = %clean_video_path,
            "视频文件不存在"
        );
        return Err(error_msg);
    }

    // 生成缩略图路径
    let thumbnail_filename = format!("{}_thumbnail.jpg", segment.id);
    let video_dir = Path::new(clean_video_path).parent()
        .ok_or("无法获取视频文件目录")?;
    let thumbnail_path = video_dir.join(thumbnail_filename);
    let thumbnail_path_str = thumbnail_path.to_string_lossy().to_string();

    // 获取视频信息来确定合适的缩略图尺寸
    let video_info = FFmpegService::get_video_info(clean_video_path)
        .map_err(|e| e.to_string())?;

    // 计算缩略图尺寸，保持宽高比，最大宽度160
    let max_width = 160;
    let (thumb_width, thumb_height) = if video_info.width > 0 && video_info.height > 0 {
        let aspect_ratio = video_info.width as f64 / video_info.height as f64;
        if video_info.width > max_width {
            let new_width = max_width;
            let new_height = (new_width as f64 / aspect_ratio).round() as u32;
            (new_width, new_height)
        } else {
            (video_info.width, video_info.height)
        }
    } else {
        // 如果无法获取视频尺寸，使用默认值
        (160, 120)
    };

    // 生成缩略图（使用首帧，带重试机制）
    let timestamp = segment.start_time;

    // 先进行预检查
    if let Err(e) = FFmpegService::validate_thumbnail_generation(
        clean_video_path,
        &thumbnail_path_str,
        timestamp
    ) {
        let error_msg = format!(
            "缩略图生成预检查失败: {} (视频: {})",
            e, clean_video_path
        );
        tracing::error!(
            segment_id = %segment_id,
            video_path = %clean_video_path,
            error = %e,
            "缩略图生成预检查失败"
        );
        return Err(error_msg);
    }

    // 使用带重试机制的缩略图生成
    if let Err(e) = FFmpegService::generate_thumbnail_with_retry(
        clean_video_path,
        &thumbnail_path_str,
        timestamp,
        thumb_width,
        thumb_height
    ) {
        let error_msg = format!(
            "FFmpeg生成缩略图失败（已重试）: {} (视频: {}, 输出: {}, 时间戳: {}s)",
            e, clean_video_path, thumbnail_path_str, timestamp
        );
        tracing::error!(
            segment_id = %segment_id,
            video_path = %clean_video_path,
            thumbnail_path = %thumbnail_path_str,
            timestamp = timestamp,
            error = %e,
            "FFmpeg缩略图生成失败（已重试）"
        );
        return Err(error_msg);
    }

    // 检查是否thumbnail_path_str是否存在 不存在报错
    if !Path::new(&thumbnail_path_str).exists() {
        let error_msg = format!("缩略图生成失败，输出文件不存在: {}", thumbnail_path_str);
        tracing::error!(
            segment_id = %segment_id,
            thumbnail_path = %thumbnail_path_str,
            "FFmpeg执行完成但缩略图文件不存在"
        );
        return Err(error_msg);
    }

    // 检查文件大小，确保不是空文件
    let file_size = fs::metadata(&thumbnail_path_str)
        .map_err(|e| format!("获取缩略图文件信息失败: {}", e))?
        .len();

    if file_size == 0 {
        let error_msg = format!("缩略图文件为空: {}", thumbnail_path_str);
        tracing::error!(
            segment_id = %segment_id,
            thumbnail_path = %thumbnail_path_str,
            "生成的缩略图文件大小为0"
        );
        return Err(error_msg);
    }

    tracing::info!(
        segment_id = %segment_id,
        thumbnail_path = %thumbnail_path_str,
        file_size = file_size,
        "缩略图文件验证成功"
    );

    // 保存缩略图路径到数据库
    {
        let material_repository_guard = app_state.material_repository.lock().unwrap();
        let material_repository = material_repository_guard.as_ref()
            .ok_or("MaterialRepository未初始化")?;

        material_repository.update_segment_thumbnail(&segment_id, &thumbnail_path_str)
            .map_err(|e| e.to_string())?;
    }

    // 读取生成的缩略图文件并返回base64
    let file_data = fs::read(&thumbnail_path_str)
        .map_err(|e| format!("读取生成的缩略图失败: {}", e))?;

    let base64_data = general_purpose::STANDARD.encode(&file_data);
    Ok(format!("data:image/jpeg;base64,{}", base64_data))
}

/// 获取音频文件的base64数据URL
#[command]
pub async fn get_audio_file_base64(
    material_id: String,
    app_state: State<'_, AppState>,
) -> Result<String, String> {
    use std::fs;
    use base64::{Engine as _, engine::general_purpose};

    // 获取素材信息
    let material = {
        let material_repository_guard = app_state.material_repository.lock().unwrap();
        let material_repository = material_repository_guard.as_ref()
            .ok_or("MaterialRepository未初始化")?;

        material_repository.get_by_id(&material_id)
            .map_err(|e| e.to_string())?
    };

    let material = match material {
        Some(m) => m,
        None => return Err("素材不存在".to_string()),
    };

    // 检查是否为音频类型
    if !matches!(material.material_type, crate::data::models::material::MaterialType::Audio) {
        return Err("不是音频类型的素材".to_string());
    }

    let audio_path = &material.original_path;

    // 去掉Windows长路径前缀
    let clean_audio_path = if audio_path.starts_with("\\\\?\\") {
        &audio_path[4..]
    } else {
        audio_path
    };

    // 检查音频文件是否存在
    if !std::path::Path::new(clean_audio_path).exists() {
        let error_msg = format!("音频文件不存在: {}", clean_audio_path);
        tracing::error!(
            material_id = %material_id,
            audio_path = %clean_audio_path,
            "音频文件不存在"
        );
        return Err(error_msg);
    }

    // 读取音频文件
    let file_data = fs::read(clean_audio_path)
        .map_err(|e| format!("读取音频文件失败: {}", e))?;

    // 根据文件扩展名确定MIME类型
    let mime_type = match std::path::Path::new(clean_audio_path)
        .extension()
        .and_then(|ext| ext.to_str())
        .map(|ext| ext.to_lowercase())
        .as_deref()
    {
        Some("mp3") => "audio/mpeg",
        Some("wav") => "audio/wav",
        Some("flac") => "audio/flac",
        Some("aac") => "audio/aac",
        Some("ogg") => "audio/ogg",
        Some("wma") => "audio/x-ms-wma",
        Some("m4a") => "audio/mp4",
        _ => "audio/mpeg", // 默认为MP3
    };

    let base64_data = general_purpose::STANDARD.encode(&file_data);
    Ok(format!("data:{};base64,{}", mime_type, base64_data))
}

/// 测试FFmpeg缩略图生成命令（用于调试）
#[command]
pub async fn test_ffmpeg_thumbnail(
    video_path: String,
    output_path: String,
    timestamp: f64,
) -> Result<String, String> {
    use crate::infrastructure::ffmpeg::FFmpegService;
    use std::path::Path;
    use tracing::{info, error};

    info!(
        video_path = %video_path,
        output_path = %output_path,
        timestamp = timestamp,
        "开始测试FFmpeg缩略图生成"
    );

    // 检查输入文件
    if !Path::new(&video_path).exists() {
        let error_msg = format!("视频文件不存在: {}", video_path);
        error!(error = %error_msg);
        return Err(error_msg);
    }

    // 获取视频信息
    let video_info = match FFmpegService::get_video_info(&video_path) {
        Ok(info) => {
            info!(
                video_path = %video_path,
                width = info.width,
                height = info.height,
                duration = info.duration,
                "视频信息获取成功"
            );
            info
        }
        Err(e) => {
            let error_msg = format!("获取视频信息失败: {}", e);
            error!(error = %error_msg);
            return Err(error_msg);
        }
    };

    // 计算缩略图尺寸
    let max_width = 160;
    let (thumb_width, thumb_height) = if video_info.width > 0 && video_info.height > 0 {
        let aspect_ratio = video_info.width as f64 / video_info.height as f64;
        if video_info.width > max_width {
            let new_width = max_width;
            let new_height = (new_width as f64 / aspect_ratio).round() as u32;
            (new_width, new_height)
        } else {
            (video_info.width, video_info.height)
        }
    } else {
        (160, 120)
    };

    // 生成缩略图
    match FFmpegService::generate_thumbnail(
        &video_path,
        &output_path,
        timestamp,
        thumb_width,
        thumb_height
    ) {
        Ok(_) => {
            // 验证文件是否生成
            if Path::new(&output_path).exists() {
                let file_size = std::fs::metadata(&output_path)
                    .map(|m| m.len())
                    .unwrap_or(0);

                let success_msg = format!(
                    "FFmpeg缩略图生成成功！\n视频文件: {}\n输出文件: {}\n时间戳: {}s\n缩略图尺寸: {}x{}\n文件大小: {} 字节",
                    video_path, output_path, timestamp, thumb_width, thumb_height, file_size
                );
                info!(message = %success_msg);
                Ok(success_msg)
            } else {
                let error_msg = "FFmpeg命令执行成功但输出文件不存在".to_string();
                error!(error = %error_msg);
                Err(error_msg)
            }
        }
        Err(e) => {
            let error_msg = format!("FFmpeg缩略图生成失败: {}", e);
            error!(error = %error_msg);
            Err(error_msg)
        }
    }
}

/// 测试文件存在性检查命令（用于调试）
#[command]
pub async fn test_file_exists(file_path: String) -> Result<String, String> {
    use std::path::Path;
    use tracing::{info, error};

    info!(file_path = %file_path, "开始检查文件是否存在");

    // 去掉Windows长路径前缀
    let clean_path = if file_path.starts_with("\\\\?\\") {
        &file_path[4..]
    } else {
        &file_path
    };

    if Path::new(clean_path).exists() {
        let metadata = std::fs::metadata(clean_path)
            .map_err(|e| format!("获取文件信息失败: {}", e))?;

        let file_size = metadata.len();
        let is_file = metadata.is_file();
        let is_dir = metadata.is_dir();

        let result = format!(
            "文件存在检查结果:\n原始路径: {}\n清理后路径: {}\n文件存在: 是\n文件大小: {} 字节\n是文件: {}\n是目录: {}",
            file_path, clean_path, file_size, is_file, is_dir
        );

        info!(
            file_path = %clean_path,
            file_size = file_size,
            is_file = is_file,
            is_dir = is_dir,
            "文件存在性检查完成"
        );

        Ok(result)
    } else {
        let error_msg = format!(
            "文件不存在:\n原始路径: {}\n清理后路径: {}\n文件存在: 否",
            file_path, clean_path
        );

        error!(
            file_path = %clean_path,
            "文件不存在"
        );

        Err(error_msg)
    }
}

/// 测试场景检测命令（用于调试）
#[command]
pub async fn test_scene_detection(file_path: String) -> Result<String, String> {
    // 首先检查FFmpeg状态
    let ffmpeg_status = FFmpegService::get_status_info().unwrap_or_else(|_| "无法获取FFmpeg状态".to_string());
    let mut result = format!("FFmpeg状态:\n{}\n", ffmpeg_status);

    // 检查文件是否存在
    if !std::path::Path::new(&file_path).exists() {
        return Ok(format!("{}文件不存在: {}", result, file_path));
    }

    result.push_str(&format!("测试文件: {}\n", file_path));

    // 尝试提取元数据
    match FFmpegService::extract_metadata(&file_path) {
        Ok(metadata) => {
            result.push_str(&format!("元数据提取成功: {:?}\n", metadata));
        }
        Err(e) => {
            result.push_str(&format!("元数据提取失败: {}\n", e));
        }
    }

    // 尝试场景检测
    match FFmpegService::detect_scenes(&file_path, 0.3) {
        Ok(scenes) => {
            result.push_str(&format!("场景检测成功，发现 {} 个场景切点: {:?}\n", scenes.len(), scenes));
        }
        Err(e) => {
            result.push_str(&format!("场景检测失败: {}\n", e));
        }
    }

    Ok(result)
}

/// 获取素材的切分片段信息命令
#[command]
pub async fn get_material_segments(
    state: State<'_, AppState>,
    material_id: String,
) -> Result<Vec<crate::data::models::material::MaterialSegment>, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    repository.get_segments(&material_id)
        .map_err(|e| e.to_string())
}

/// 根据片段ID获取片段详细信息命令
#[command]
pub async fn get_material_segment_by_id(
    state: State<'_, AppState>,
    segment_id: String,
) -> Result<Option<crate::data::models::material::MaterialSegment>, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    repository.get_segment_by_id_sync(&segment_id)
        .map_err(|e| e.to_string())
}

/// 测试视频切分命令（用于调试不同切分模式）
#[command]
pub async fn test_video_split(
    input_path: String,
    output_dir: String,
    start_time: f64,
    end_time: f64,
    mode: String,
) -> Result<String, String> {
    use crate::infrastructure::ffmpeg::FFmpegService;

    let segments = vec![(start_time, end_time)];
    let output_prefix = "test_segment";

    let result = match mode.as_str() {
        "fast" => {
            FFmpegService::split_video_fast(&input_path, &output_dir, &segments, output_prefix)
        }
        "accurate" => {
            FFmpegService::split_video(&input_path, &output_dir, &segments, output_prefix)
        }
        "smart" => {
            FFmpegService::split_video_smart(&input_path, &output_dir, &segments, output_prefix)
        }
        _ => return Err("无效的切分模式，支持: fast, accurate, smart".to_string()),
    };

    match result {
        Ok(files) => Ok(format!("切分成功，生成文件: {:?}", files)),
        Err(e) => Err(format!("切分失败: {}", e)),
    }
}

/// 关联素材到模特命令
#[command]
pub async fn associate_material_to_model(
    state: State<'_, AppState>,
    material_id: String,
    model_id: String,
) -> Result<(), String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    repository.associate_material_to_model(&material_id, &model_id)
        .map_err(|e| format!("关联素材到模特失败: {}", e))
}

/// 取消素材与模特的关联命令
#[command]
pub async fn disassociate_material_from_model(
    state: State<'_, AppState>,
    material_id: String,
) -> Result<(), String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    repository.disassociate_material_from_model(&material_id)
        .map_err(|e| format!("取消关联失败: {}", e))
}

/// 根据模特ID获取关联的素材命令
#[command]
pub async fn get_materials_by_model_id(
    state: State<'_, AppState>,
    model_id: String,
) -> Result<Vec<Material>, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    repository.get_materials_by_model_id(&model_id)
        .map_err(|e| format!("获取模特关联素材失败: {}", e))
}

/// 获取未关联模特的素材命令
#[command]
pub async fn get_unassociated_materials(
    state: State<'_, AppState>,
    project_id: Option<String>,
) -> Result<Vec<Material>, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    repository.get_unassociated_materials(project_id.as_deref())
        .map_err(|e| format!("获取未关联素材失败: {}", e))
}

/// 批量绑定素材到模特命令
#[command]
pub async fn batch_bind_materials_to_model(
    state: State<'_, AppState>,
    material_ids: Vec<String>,
    model_id: String,
) -> Result<(), String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::batch_bind_materials_to_model(repository, &material_ids, &model_id)
        .map_err(|e| format!("批量绑定素材到模特失败: {}", e))
}

/// 批量解除素材与模特的绑定命令
#[command]
pub async fn batch_unbind_materials_from_model(
    state: State<'_, AppState>,
    material_ids: Vec<String>,
) -> Result<(), String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::batch_unbind_materials_from_model(repository, &material_ids)
        .map_err(|e| format!("批量解除绑定失败: {}", e))
}

/// 切换素材的模特绑定命令
#[command]
pub async fn switch_material_model(
    state: State<'_, AppState>,
    material_id: String,
    new_model_id: String,
) -> Result<(), String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::switch_material_model(repository, &material_id, &new_model_id)
        .map_err(|e| format!("切换素材模特绑定失败: {}", e))
}

/// 获取模特的素材统计信息命令
#[command]
pub async fn get_model_material_statistics(
    state: State<'_, AppState>,
    model_id: String,
) -> Result<crate::data::repositories::material_repository::ModelMaterialStatistics, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::get_model_material_statistics(repository, &model_id)
        .map_err(|e| format!("获取模特素材统计失败: {}", e))
}

/// 获取项目中模特绑定统计信息命令
#[command]
pub async fn get_project_model_binding_stats(
    state: State<'_, AppState>,
    project_id: String,
) -> Result<crate::business::services::material_service::ProjectModelBindingStats, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::get_project_model_binding_stats(repository, &project_id)
        .map_err(|e| format!("获取项目模特绑定统计失败: {}", e))
}

/// 获取全局模特绑定统计信息命令
#[command]
pub async fn get_global_model_binding_stats(
    state: State<'_, AppState>,
) -> Result<crate::business::services::material_service::ProjectModelBindingStats, String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    MaterialService::get_global_model_binding_stats(repository)
        .map_err(|e| format!("获取全局模特绑定统计失败: {}", e))
}

/// 更新素材信息命令（包括模特绑定）
#[command]
pub async fn update_material(
    state: State<'_, AppState>,
    id: String,
    updates: MaterialUpdateRequest,
) -> Result<(), String> {
    let repository_guard = state.get_material_repository()
        .map_err(|e| format!("获取素材仓库失败: {}", e))?;

    let repository = repository_guard.as_ref()
        .ok_or("素材仓库未初始化")?;

    // 获取现有素材
    let mut material = repository.get_by_id(&id)
        .map_err(|e| format!("获取素材失败: {}", e))?
        .ok_or("找不到指定的素材")?;

    // 更新字段
    if let Some(model_id) = updates.model_id {
        material.model_id = if model_id.is_empty() { None } else { Some(model_id) };
    }
    if let Some(name) = updates.name {
        material.name = name;
    }

    // 更新时间戳
    material.updated_at = chrono::Utc::now();

    // 保存更新
    repository.update(&material)
        .map_err(|e| format!("更新素材失败: {}", e))?;

    Ok(())
}

/// 素材更新请求
#[derive(Debug, serde::Deserialize)]
pub struct MaterialUpdateRequest {
    pub model_id: Option<String>,
    pub name: Option<String>,
}
