use anyhow::{Result, anyhow};
use std::path::Path;
use std::fs;
use std::time::Instant;
use std::sync::Arc;

use crate::data::models::material::{
    Material, MaterialType, ProcessingStatus, CreateMaterialRequest,
    MaterialImportResult, MaterialProcessingConfig, MaterialMetadata,
    BatchDeleteResult, BatchDeleteFailedItem
};
use crate::data::repositories::material_repository::MaterialRepository;
use crate::infrastructure::ffmpeg::FFmpegService;
use crate::infrastructure::monitoring::PERFORMANCE_MONITOR;
use crate::business::errors::error_utils;
use tracing::{info, warn, error, debug};

/// 素材服务
/// 遵循 Tauri 开发规范的业务逻辑层设计
pub struct MaterialService;

impl MaterialService {
    /// 导入素材文件
    pub fn import_materials(
        repository: &MaterialRepository,
        request: CreateMaterialRequest,
        config: &MaterialProcessingConfig,
    ) -> Result<MaterialImportResult> {
        let timer = PERFORMANCE_MONITOR.start_operation("import_materials");
        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,
        };

        for file_path in &request.file_paths {
            debug!(file_path = %file_path, "处理文件");

            match Self::process_single_file(repository, &request.project_id, file_path, config, request.model_id.clone()) {
                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));
                }
            }
        }

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

        let success = result.failed_files == 0;
        timer.finish(success);

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

        PERFORMANCE_MONITOR.record_metric("import_files_per_second",
            result.total_files as f64 / result.processing_time);

        Ok(result)
    }

    /// 处理单个文件
    fn process_single_file(
        repository: &MaterialRepository,
        project_id: &str,
        file_path: &str,
        config: &MaterialProcessingConfig,
        model_id: Option<String>,
    ) -> Result<Option<Material>> {
        let _timer = PERFORMANCE_MONITOR.start_operation("process_single_file");

        // 验证文件路径
        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 md5_hash = Self::calculate_md5(file_path)?;
        
        // 检查是否已存在相同的文件
        if repository.exists_by_md5(project_id, &md5_hash)? {
            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 material = Material::new_with_model(
            project_id.to_string(),
            file_name,
            file_path.to_string(),
            file_size,
            md5_hash,
            material_type,
            model_id,
        );

        // 保存到数据库
        repository.create(&material)?;

        // 如果启用自动处理，则开始处理
        if config.auto_process.unwrap_or(true) {
            // 同步处理素材（提取元数据、场景检测等）
            match Self::process_material(repository, &material.id, config) {
                Ok(_) => {
                    // 处理成功，重新获取更新后的素材
                    if let Ok(Some(updated_material)) = repository.get_by_id(&material.id) {
                        return Ok(Some(updated_material));
                    }
                }
                Err(e) => {
                    // 处理失败，但不影响导入结果，只记录错误
                    eprintln!("素材处理失败: {} - {}", material.name, e);
                }
            }
        }

        Ok(Some(material))
    }

    /// 计算文件的MD5哈希值
    pub fn calculate_md5(file_path: &str) -> Result<String> {
        let data = fs::read(file_path)?;
        let digest = md5::compute(&data);
        Ok(format!("{:x}", digest))
    }

    /// 获取项目的所有素材
    pub fn get_project_materials(
        repository: &MaterialRepository,
        project_id: &str,
    ) -> Result<Vec<Material>> {
        let mut materials = repository.get_by_project_id(project_id)?;

        // 为每个素材加载片段信息
        for material in &mut materials {
            material.segments = repository.get_segments(&material.id)?;
        }

        Ok(materials)
    }

    /// 获取所有素材
    pub fn get_all_materials(
        repository: &MaterialRepository,
    ) -> Result<Vec<Material>> {
        let mut materials = repository.get_all()?;

        // 为每个素材加载片段信息
        for material in &mut materials {
            material.segments = repository.get_segments(&material.id)?;
        }

        Ok(materials)
    }

    /// 获取素材详情
    pub fn get_material_by_id(
        repository: &MaterialRepository,
        id: &str,
    ) -> Result<Option<Material>> {
        if let Some(mut material) = repository.get_by_id(id)? {
            // 加载片段信息
            material.segments = repository.get_segments(&material.id)?;
            Ok(Some(material))
        } else {
            Ok(None)
        }
    }

    /// 删除素材
    pub fn delete_material(
        repository: &MaterialRepository,
        id: &str,
    ) -> Result<()> {
        // TODO: 删除相关的文件
        repository.delete(id)?;
        Ok(())
    }

    /// 批量删除素材
    /// 返回删除结果统计信息
    pub fn batch_delete_materials(
        repository: &MaterialRepository,
        material_ids: Vec<String>,
    ) -> Result<BatchDeleteResult> {
        info!(
            material_count = material_ids.len(),
            "开始批量删除素材"
        );

        // 参数验证
        if material_ids.is_empty() {
            return Err(anyhow!("素材ID列表不能为空"));
        }

        if material_ids.len() > 100 {
            return Err(anyhow!("单次批量删除不能超过100个素材"));
        }

        // 执行批量删除
        let (successful_deletes, failed_deletes) = repository.batch_delete(&material_ids)?;

        let result = BatchDeleteResult {
            total_count: material_ids.len(),
            success_count: successful_deletes.len(),
            failed_count: failed_deletes.len(),
            successful_ids: successful_deletes,
            failed_items: failed_deletes.into_iter().map(|(id, error)| BatchDeleteFailedItem {
                id,
                error_message: error,
            }).collect(),
        };

        info!(
            total_count = result.total_count,
            success_count = result.success_count,
            failed_count = result.failed_count,
            "批量删除素材完成"
        );

        Ok(result)
    }

    /// 更新素材处理状态
    pub fn update_material_status(
        repository: &MaterialRepository,
        id: &str,
        status: ProcessingStatus,
        error_message: Option<String>,
    ) -> Result<()> {
        if let Some(mut material) = repository.get_by_id(id)? {
            material.update_status(status, error_message);
            repository.update(&material)?;
        }
        Ok(())
    }

    /// 获取项目素材统计
    pub fn get_project_stats(
        repository: &MaterialRepository,
        project_id: &str,
    ) -> Result<crate::data::models::material::MaterialStats> {
        repository.get_project_stats(project_id)
    }

    /// 验证文件是否为支持的格式
    pub fn is_supported_format(file_path: &str) -> bool {
        let path = Path::new(file_path);
        if let Some(extension) = path.extension().and_then(|ext| ext.to_str()) {
            let material_type = MaterialType::from_extension(extension);
            !matches!(material_type, MaterialType::Other)
        } else {
            false
        }
    }

    /// 获取支持的文件扩展名列表
    pub fn get_supported_extensions() -> Vec<&'static str> {
        vec![
            // 视频格式
            "mp4", "avi", "mov", "mkv", "wmv", "flv", "webm", "m4v",
            // 音频格式
            "mp3", "wav", "flac", "aac", "ogg", "wma", "m4a",
            // 图片格式
            "jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp", "svg",
            // 文档格式
            "pdf", "doc", "docx", "txt", "md", "rtf",
        ]
    }

    /// 批量处理素材
    pub fn batch_process_materials(
        repository: &MaterialRepository,
        material_ids: Vec<String>,
        config: &MaterialProcessingConfig,
    ) -> Result<Vec<String>> {
        let mut processed_ids = Vec::new();
        
        for material_id in material_ids {
            match Self::process_material(repository, &material_id, config) {
                Ok(_) => {
                    processed_ids.push(material_id);
                }
                Err(e) => {
                    // 更新状态为失败
                    let _ = Self::update_material_status(
                        repository,
                        &material_id,
                        ProcessingStatus::Failed,
                        Some(e.to_string()),
                    );
                }
            }
        }
        
        Ok(processed_ids)
    }

    /// 处理单个素材（提取元数据、场景检测等）
    fn process_material(
        repository: &MaterialRepository,
        material_id: &str,
        config: &MaterialProcessingConfig,
    ) -> Result<()> {
        // 更新状态为处理中
        Self::update_material_status(
            repository,
            material_id,
            ProcessingStatus::Processing,
            None,
        )?;

        // 获取素材信息
        let mut material = repository.get_by_id(material_id)?
            .ok_or_else(|| anyhow!("素材不存在: {}", material_id))?;

        // 1. 提取元数据
        match Self::extract_metadata(&material.original_path, &material.material_type) {
            Ok(metadata) => {
                material.set_metadata(metadata);
                repository.update(&material)?;
            }
            Err(e) => {
                Self::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 {
            println!("开始视频场景检测: {}", material.original_path);

            // 如果设置了跳过开头，先创建临时视频文件
            let detection_file_path = if let Some(skip_ms) = config.skip_start_ms {
                if skip_ms > 0 {
                    println!("AI生成视频前置跳过: {}ms", skip_ms);
                    match crate::infrastructure::ffmpeg::FFmpegService::create_trimmed_video(&material.original_path, skip_ms) {
                        Ok(temp_path) => {
                            println!("临时视频创建成功，用于场景检测: {}", temp_path);
                            temp_path
                        }
                        Err(e) => {
                            eprintln!("创建临时视频失败，使用原视频: {}", e);
                            material.original_path.clone()
                        }
                    }
                } else {
                    material.original_path.clone()
                }
            } else {
                material.original_path.clone()
            };

            match Self::detect_video_scenes(&detection_file_path, config.scene_detection_threshold) {
                Ok(mut scene_detection) => {
                    // 如果使用了临时文件，需要调整场景时间戳
                    if let Some(skip_ms) = config.skip_start_ms {
                        if skip_ms > 0 && detection_file_path != material.original_path {
                            let skip_seconds = skip_ms as f64 / 1000.0;
                            println!("调整场景时间戳，补偿跳过的{}秒", skip_seconds);
                            for scene in &mut scene_detection.scenes {
                                scene.start_time += skip_seconds;
                                scene.end_time += skip_seconds;
                            }
                        }
                    }

                    println!("场景检测成功，发现 {} 个场景", scene_detection.scenes.len());
                    material.set_scene_detection(scene_detection);
                    repository.update(&material)?;

                    // 清理临时文件
                    if detection_file_path != material.original_path {
                        if let Err(e) = std::fs::remove_file(&detection_file_path) {
                            eprintln!("清理临时文件失败: {}", e);
                        } else {
                            println!("临时文件清理成功: {}", detection_file_path);
                        }
                    }
                }
                Err(e) => {
                    // 场景检测失败不应该导致整个处理失败
                    eprintln!("场景检测失败: {}", e);

                    // 清理临时文件
                    if detection_file_path != material.original_path {
                        if let Err(e) = std::fs::remove_file(&detection_file_path) {
                            eprintln!("清理临时文件失败: {}", e);
                        }
                    }
                }
            }
        } else {
            println!("跳过场景检测 - 视频类型: {}, 启用检测: {}",
                matches!(material.material_type, MaterialType::Video),
                config.enable_scene_detection);
        }

        // 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 {
            println!("开始视频切分 - 总时长检查: {}, 场景检测: {}",
                material.needs_segmentation(config.max_segment_duration),
                material.scene_detection.is_some());

            match Self::segment_video(repository, &material, config) {
                Ok(_) => {
                    println!("视频切分完成: {}", material.name);
                }
                Err(e) => {
                    Self::update_material_status(
                        repository,
                        material_id,
                        ProcessingStatus::Failed,
                        Some(format!("视频切分失败: {}", e)),
                    )?;
                    return Err(e);
                }
            }
        } else {
            println!("跳过视频切分 - 不满足切分条件");
        }

        // 标记为完成
        Self::update_material_status(
            repository,
            material_id,
            ProcessingStatus::Completed,
            None,
        )?;

        Ok(())
    }

    /// 提取素材元数据
    pub fn extract_metadata(file_path: &str, material_type: &MaterialType) -> Result<MaterialMetadata> {
        match material_type {
            MaterialType::Video | MaterialType::Audio => {
                // 使用 FFmpeg 提取视频/音频元数据
                FFmpegService::extract_metadata(file_path)
            }
            MaterialType::Image => {
                // TODO: 实现图片元数据提取
                Ok(MaterialMetadata::None)
            }
            _ => {
                // 其他类型不需要元数据
                Ok(MaterialMetadata::None)
            }
        }
    }

    /// 检测视频场景
    pub fn detect_video_scenes(
        file_path: &str,
        threshold: f64,
    ) -> Result<crate::data::models::material::SceneDetection> {
        let scene_times = FFmpegService::detect_scenes(file_path, threshold)?;

        // 获取视频总时长
        let total_duration = if let Ok(metadata) = Self::extract_metadata(file_path, &MaterialType::Video) {
            if let MaterialMetadata::Video(video_meta) = metadata {
                video_meta.duration
            } else {
                return Err(anyhow!("无法获取视频元数据"));
            }
        } else {
            return Err(anyhow!("无法提取视频元数据"));
        };

        let mut scenes = Vec::new();
        let mut previous_time = 0.0;

        // 根据场景切换点创建场景片段
        for (index, &scene_time) in scene_times.iter().enumerate() {
            if scene_time > previous_time {
                scenes.push(crate::data::models::material::SceneSegment {
                    start_time: previous_time,
                    end_time: scene_time,
                    duration: scene_time - previous_time,
                    scene_id: index as u32,
                    confidence: 1.0,
                });
                previous_time = scene_time;
            }
        }

        // 添加最后一个场景片段
        if previous_time < total_duration {
            scenes.push(crate::data::models::material::SceneSegment {
                start_time: previous_time,
                end_time: total_duration,
                duration: total_duration - previous_time,
                scene_id: scenes.len() as u32,
                confidence: 1.0,
            });
        }

        println!("场景检测完成，共 {} 个场景:", scenes.len());
        for (i, scene) in scenes.iter().enumerate() {
            println!("  场景 {}: {:.2}s - {:.2}s (时长: {:.2}s)",
                i + 1, scene.start_time, scene.end_time, scene.duration);
        }

        let total_scenes = scenes.len() as u32;

        Ok(crate::data::models::material::SceneDetection {
            scenes,
            total_scenes,
            detection_method: "ffmpeg_scene_filter".to_string(),
            threshold,
        })
    }

    /// 切分视频
    pub fn segment_video(
        repository: &MaterialRepository,
        material: &Material,
        config: &MaterialProcessingConfig,
    ) -> Result<()> {
        // 首先获取项目信息来确定输出路径
        let project = Self::get_project_for_material(&material.project_id)?;
        // 第一步：根据场景检测结果进行分镜头切分
        let primary_segments = if let Some(scene_detection) = &material.scene_detection {
            // 使用场景检测结果，每个场景作为一个片段
            println!("使用场景检测结果进行分镜头切分，共 {} 个场景", scene_detection.scenes.len());
            Self::create_segments_from_scenes_direct(&scene_detection.scenes)
        } else {
            // 没有场景检测结果，使用固定时长切分
            if let Some(duration) = material.get_duration() {
                println!("没有场景检测结果，使用固定时长切分");
                Self::create_fixed_segments(duration, config.max_segment_duration)
            } else {
                return Err(anyhow!("无法获取视频时长"));
            }
        };

        // 第二步：对超长的分镜头进行二次切分
        let final_segments = Self::apply_duration_limit(&primary_segments, config.max_segment_duration);

        println!("分镜头切分完成：{} 个片段", primary_segments.len());
        println!("二次切分后：{} 个片段", final_segments.len());

        // 创建输出目录：项目目录/pending/素材名称_segments (使用英文避免中文路径问题)
        let mut project_path = project.path.clone();

        // 处理 Windows 长路径格式，移除 \\?\ 前缀
        if project_path.starts_with("\\\\?\\") {
            project_path = project_path[4..].to_string();
        }

        let pending_dir = std::path::Path::new(&project_path).join("pending");

        // 确保pending目录存在
        if !pending_dir.exists() {
            std::fs::create_dir_all(&pending_dir)
                .map_err(|e| anyhow!("创建pending目录失败: {}", e))?;
            println!("创建pending目录: {:?}", pending_dir);
        }

        let material_name = material.name.trim_end_matches(".mp4");
        let output_dir = pending_dir.join(format!("{}_segments", material_name));

        // 标准化路径，确保FFmpeg可以处理
        let output_dir_str = output_dir.to_string_lossy().to_string();
        let normalized_output_dir = if output_dir_str.starts_with("\\\\?\\") {
            output_dir_str[4..].to_string()
        } else {
            output_dir_str
        };

        println!("视频切分输出目录: {}", normalized_output_dir);

        // 根据配置选择切分模式
        let output_files = match config.split_mode {
            crate::data::models::material::VideoSplitMode::Fast => {
                println!("使用快速切分模式（可能有前几秒无画面问题）");
                FFmpegService::split_video_fast(
                    &material.original_path,
                    &normalized_output_dir,
                    &final_segments,
                    &material.name.replace(".mp4", ""),
                )?
            }
            crate::data::models::material::VideoSplitMode::Accurate => {
                println!("使用精确切分模式（重新编码，确保画面完整）");
                FFmpegService::split_video(
                    &material.original_path,
                    &normalized_output_dir,
                    &final_segments,
                    &material.name.replace(".mp4", ""),
                )?
            }
            crate::data::models::material::VideoSplitMode::Smart => {
                println!("使用智能切分模式（关键帧对齐）");
                FFmpegService::split_video_smart(
                    &material.original_path,
                    &normalized_output_dir,
                    &final_segments,
                    &material.name.replace(".mp4", ""),
                )?
            }
        };

        // 保存片段信息到数据库
        for (index, output_file) in output_files.iter().enumerate() {
            let (start_time, end_time) = final_segments[index];
            let file_size = std::fs::metadata(output_file)?.len();

            let segment = crate::data::models::material::MaterialSegment::new(
                material.id.clone(),
                index as u32,
                start_time,
                end_time,
                output_file.clone(),
                file_size,
            );

            repository.create_segment(&segment)?;
        }

        Ok(())
    }

    /// 直接根据场景创建分镜头片段（第一次切分）
    pub fn create_segments_from_scenes_direct(
        scenes: &[crate::data::models::material::SceneSegment],
    ) -> Vec<(f64, f64)> {
        let mut segments = Vec::new();

        for scene in scenes {
            segments.push((scene.start_time, scene.end_time));
        }

        println!("分镜头切分：根据 {} 个场景创建 {} 个片段", scenes.len(), segments.len());
        for (i, (start, end)) in segments.iter().enumerate() {
            println!("  分镜头 {}: {:.2}s - {:.2}s (时长: {:.2}s)",
                i + 1, start, end, end - start);
        }

        segments
    }

    /// 对片段应用时长限制（二次切分）
    pub fn apply_duration_limit(
        segments: &[(f64, f64)],
        max_duration: f64,
    ) -> Vec<(f64, f64)> {
        let mut final_segments = Vec::new();

        println!("开始二次切分，最大时长限制: {}秒", max_duration);

        for (i, (start, end)) in segments.iter().enumerate() {
            let duration = end - start;

            if duration > max_duration {
                println!("  分镜头 {} 超长 ({:.2}s > {:.2}s)，进行二次切分",
                    i + 1, duration, max_duration);

                // 将超长片段按最大时长切分
                let sub_segments = Self::create_fixed_segments_range(*start, *end, max_duration);

                println!("    切分为 {} 个子片段:", sub_segments.len());
                for (j, (sub_start, sub_end)) in sub_segments.iter().enumerate() {
                    println!("      子片段 {}: {:.2}s - {:.2}s (时长: {:.2}s)",
                        j + 1, sub_start, sub_end, sub_end - sub_start);
                }

                final_segments.extend(sub_segments);
            } else {
                println!("  分镜头 {} 时长合适 ({:.2}s ≤ {:.2}s)，保持不变",
                    i + 1, duration, max_duration);
                final_segments.push((*start, *end));
            }
        }

        println!("二次切分完成，最终 {} 个片段", final_segments.len());
        final_segments
    }

    /// 根据场景创建切分片段（旧版本，保留兼容性）
    pub fn create_segments_from_scenes(
        scenes: &[crate::data::models::material::SceneSegment],
        max_duration: f64,
    ) -> Vec<(f64, f64)> {
        // 使用新的两步法
        let primary_segments = Self::create_segments_from_scenes_direct(scenes);
        Self::apply_duration_limit(&primary_segments, max_duration)
    }

    /// 创建固定时长的切分片段
    pub fn create_fixed_segments(total_duration: f64, max_duration: f64) -> Vec<(f64, f64)> {
        Self::create_fixed_segments_range(0.0, total_duration, max_duration)
    }

    /// 在指定时间范围内创建固定时长的切分片段
    fn create_fixed_segments_range(start_time: f64, end_time: f64, max_duration: f64) -> Vec<(f64, f64)> {
        let mut segments = Vec::new();
        let mut current_time = start_time;

        while current_time < end_time {
            let segment_end = (current_time + max_duration).min(end_time);
            segments.push((current_time, segment_end));
            current_time = segment_end;
        }

        segments
    }

    /// 清理失效的素材记录
    pub fn cleanup_invalid_materials(
        repository: &MaterialRepository,
        project_id: &str,
    ) -> Result<u32> {
        let materials = repository.get_by_project_id(project_id)?;
        let mut cleaned_count = 0;

        for material in materials {
            // 检查原始文件是否还存在
            if !Path::new(&material.original_path).exists() {
                repository.delete(&material.id)?;
                cleaned_count += 1;
            }
        }

        Ok(cleaned_count)
    }

    /// 获取素材对应的项目信息
    fn get_project_for_material(project_id: &str) -> Result<crate::data::models::project::Project> {
        use crate::infrastructure::database::Database;
        use crate::data::repositories::project_repository::ProjectRepository;
        use crate::business::services::project_service::ProjectService;

        // 创建数据库连接
        let db = Arc::new(Database::new()?);
        let project_repo = ProjectRepository::new(db.clone())?;

        // 获取项目信息
        ProjectService::get_project_by_id(&project_repo, project_id)?
            .ok_or_else(|| anyhow!("找不到项目: {}", project_id))
    }

    /// 绑定素材到模特
    pub fn bind_material_to_model(
        repository: &MaterialRepository,
        material_id: &str,
        model_id: &str,
    ) -> Result<()> {
        info!(
            material_id = %material_id,
            model_id = %model_id,
            "开始绑定素材到模特"
        );

        // 验证素材是否存在
        let material = repository.get_by_id(material_id)?
            .ok_or_else(|| anyhow!("找不到素材: {}", material_id))?;

        // 验证模特是否存在
        Self::validate_model_exists(model_id)?;

        // 执行绑定
        repository.update_model_binding(material_id, Some(model_id))?;

        info!(
            material_id = %material_id,
            model_id = %model_id,
            material_name = %material.name,
            "素材绑定到模特成功"
        );

        Ok(())
    }

    /// 解除素材与模特的绑定
    pub fn unbind_material_from_model(
        repository: &MaterialRepository,
        material_id: &str,
    ) -> Result<()> {
        info!(material_id = %material_id, "开始解除素材与模特的绑定");

        // 验证素材是否存在
        let material = repository.get_by_id(material_id)?
            .ok_or_else(|| anyhow!("找不到素材: {}", material_id))?;

        // 执行解绑
        repository.update_model_binding(material_id, None)?;

        info!(
            material_id = %material_id,
            material_name = %material.name,
            "素材与模特解绑成功"
        );

        Ok(())
    }

    /// 批量绑定素材到模特
    pub fn batch_bind_materials_to_model(
        repository: &MaterialRepository,
        material_ids: &[String],
        model_id: &str,
    ) -> Result<()> {
        info!(
            material_count = material_ids.len(),
            model_id = %model_id,
            "开始批量绑定素材到模特"
        );

        // 验证模特是否存在
        Self::validate_model_exists(model_id)?;

        // 验证所有素材是否存在
        for material_id in material_ids {
            if repository.get_by_id(material_id)?.is_none() {
                return Err(anyhow!("找不到素材: {}", material_id));
            }
        }

        // 执行批量绑定
        repository.batch_update_model_binding(material_ids, Some(model_id))?;

        info!(
            material_count = material_ids.len(),
            model_id = %model_id,
            "批量绑定素材到模特成功"
        );

        Ok(())
    }

    /// 批量解除素材与模特的绑定
    pub fn batch_unbind_materials_from_model(
        repository: &MaterialRepository,
        material_ids: &[String],
    ) -> Result<()> {
        info!(
            material_count = material_ids.len(),
            "开始批量解除素材与模特的绑定"
        );

        // 验证所有素材是否存在
        for material_id in material_ids {
            if repository.get_by_id(material_id)?.is_none() {
                return Err(anyhow!("找不到素材: {}", material_id));
            }
        }

        // 执行批量解绑
        repository.batch_update_model_binding(material_ids, None)?;

        info!(
            material_count = material_ids.len(),
            "批量解除素材与模特绑定成功"
        );

        Ok(())
    }

    /// 获取模特的所有素材
    pub fn get_materials_by_model(
        repository: &MaterialRepository,
        model_id: &str,
    ) -> Result<Vec<Material>> {
        debug!(model_id = %model_id, "获取模特的所有素材");

        // 验证模特是否存在
        Self::validate_model_exists(model_id)?;

        repository.get_by_model_id(model_id)
    }

    /// 获取未绑定模特的素材
    pub fn get_unbound_materials(
        repository: &MaterialRepository,
        project_id: Option<&str>,
    ) -> Result<Vec<Material>> {
        debug!(project_id = ?project_id, "获取未绑定模特的素材");

        if let Some(pid) = project_id {
            repository.get_unassociated_materials(Some(pid))
        } else {
            repository.get_unbound_materials()
        }
    }

    /// 获取模特的素材统计信息
    pub fn get_model_material_statistics(
        repository: &MaterialRepository,
        model_id: &str,
    ) -> Result<crate::data::repositories::material_repository::ModelMaterialStatistics> {
        debug!(model_id = %model_id, "获取模特的素材统计信息");

        // 验证模特是否存在
        Self::validate_model_exists(model_id)?;

        repository.get_model_statistics(model_id)
    }

    /// 验证模特是否存在
    fn validate_model_exists(model_id: &str) -> Result<()> {
        use crate::infrastructure::database::Database;
        use crate::data::repositories::model_repository::ModelRepository;

        let db = Arc::new(Database::new()?);
        let model_repo = ModelRepository::new(db.clone());

        if model_repo.get_by_id(model_id)?.is_none() {
            return Err(anyhow!("找不到模特: {}", model_id));
        }

        Ok(())
    }

    /// 切换素材的模特绑定
    pub fn switch_material_model(
        repository: &MaterialRepository,
        material_id: &str,
        new_model_id: &str,
    ) -> Result<()> {
        info!(
            material_id = %material_id,
            new_model_id = %new_model_id,
            "开始切换素材的模特绑定"
        );

        // 验证素材是否存在
        let material = repository.get_by_id(material_id)?
            .ok_or_else(|| anyhow!("找不到素材: {}", material_id))?;

        // 验证新模特是否存在
        Self::validate_model_exists(new_model_id)?;

        // 执行切换
        repository.update_model_binding(material_id, Some(new_model_id))?;

        info!(
            material_id = %material_id,
            material_name = %material.name,
            new_model_id = %new_model_id,
            "素材模特绑定切换成功"
        );

        Ok(())
    }

    /// 获取项目中绑定模特的素材统计
    pub fn get_project_model_binding_stats(
        repository: &MaterialRepository,
        project_id: &str,
    ) -> Result<ProjectModelBindingStats> {
        debug!(project_id = %project_id, "获取项目中模特绑定统计");

        let bound_count = repository.count_bound_materials_in_project(project_id)?;
        let unbound_count = repository.count_unbound_materials_in_project(project_id)?;
        let total_count = bound_count + unbound_count;

        Ok(ProjectModelBindingStats {
            total_materials: total_count,
            bound_materials: bound_count,
            unbound_materials: unbound_count,
            binding_rate: if total_count > 0 {
                (bound_count as f64 / total_count as f64) * 100.0
            } else {
                0.0
            },
        })
    }

    /// 获取全局模特绑定统计
    pub fn get_global_model_binding_stats(
        repository: &MaterialRepository,
    ) -> Result<ProjectModelBindingStats> {
        debug!("获取全局模特绑定统计");

        let bound_count = repository.count_global_bound_materials()?;
        let unbound_count = repository.count_global_unbound_materials()?;
        let total_count = bound_count + unbound_count;

        Ok(ProjectModelBindingStats {
            total_materials: total_count,
            bound_materials: bound_count,
            unbound_materials: unbound_count,
            binding_rate: if total_count > 0 {
                (bound_count as f64 / total_count as f64) * 100.0
            } else {
                0.0
            },
        })
    }
}

/// 项目模特绑定统计信息
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ProjectModelBindingStats {
    pub total_materials: u32,
    pub bound_materials: u32,
    pub unbound_materials: u32,
    pub binding_rate: f64, // 绑定率百分比
}
