/**
 * 素材匹配服务
 * 遵循 Tauri 开发规范的业务逻辑层设计原则
 */

use crate::data::models::{
    material::{Material, MaterialSegment},
    template::{Template, TrackSegment, SegmentMatchingRule},
    video_classification::VideoClassificationRecord,
};
use crate::data::repositories::{
    material_repository::MaterialRepository,
    material_usage_repository::MaterialUsageRepository,
    video_classification_repository::VideoClassificationRepository,
};
use crate::business::services::template_service::TemplateService;
use crate::business::services::template_matching_result_service::TemplateMatchingResultService;
use crate::business::services::template_segment_weight_service::TemplateSegmentWeightService;
use crate::infrastructure::filename_utils::FilenameUtils;
use crate::infrastructure::event_bus::EventBusManager;
use tauri::Emitter;
use anyhow::{Result, anyhow};
use serde::{Serialize, Deserialize};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;

/// 素材匹配服务
pub struct MaterialMatchingService {
    material_repo: Arc<MaterialRepository>,
    material_usage_repo: Arc<MaterialUsageRepository>,
    template_service: Arc<TemplateService>,
    video_classification_repo: Arc<VideoClassificationRepository>,
    ai_classification_service: Arc<crate::business::services::ai_classification_service::AiClassificationService>,
    template_segment_weight_service: Option<Arc<TemplateSegmentWeightService>>,
    matching_result_service: Option<Arc<TemplateMatchingResultService>>,
    event_bus: Arc<EventBusManager>,
}

/// 素材匹配请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialMatchingRequest {
    pub project_id: String,
    pub template_id: String,
    pub binding_id: String,
    pub overwrite_existing: bool,
}

/// 素材匹配结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialMatchingResult {
    pub binding_id: String,
    pub template_id: String,
    pub project_id: String,
    pub matches: Vec<SegmentMatch>,
    pub statistics: MatchingStatistics,
    pub failed_segments: Vec<FailedSegmentMatch>,
}

/// 一键匹配请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchMatchingRequest {
    pub project_id: String,
    pub overwrite_existing: bool,
    pub result_name_prefix: Option<String>, // 结果名称前缀，默认为"一键匹配"
}

/// 一键匹配结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchMatchingResult {
    pub project_id: String,
    pub total_bindings: u32,
    pub successful_matches: u32,
    pub failed_matches: u32,
    pub skipped_bindings: u32,
    pub matching_results: Vec<BatchMatchingItemResult>,
    pub total_duration_ms: u64,
    pub summary: BatchMatchingSummary,
    // 新增循环匹配相关字段
    pub total_rounds: u32,           // 总循环轮数
    pub successful_rounds: u32,      // 成功匹配的轮数
    pub termination_reason: String,  // 循环终止原因
    pub materials_exhausted: bool,   // 是否因素材耗尽而终止
}

/// 单个绑定的匹配结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchMatchingItemResult {
    pub binding_id: String,
    pub template_id: String,
    pub template_name: String,
    pub binding_name: Option<String>,
    pub status: BatchMatchingItemStatus,
    pub matching_result: Option<MaterialMatchingResult>,
    pub saved_result_id: Option<String>,
    pub error_message: Option<String>,
    pub duration_ms: u64,
    // 新增循环匹配相关字段
    pub round_number: u32,           // 匹配成功的轮次
    pub attempts_count: u32,         // 尝试匹配的次数
    pub failure_reason: Option<String>, // 详细失败原因
}

/// 单个绑定匹配状态
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BatchMatchingItemStatus {
    Success,
    Failed,
    Skipped,
}

/// 一键匹配汇总信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchMatchingSummary {
    pub total_segments_matched: u32,
    pub total_materials_used: u32,
    pub total_models_used: u32,
    pub average_success_rate: f64,
    pub best_matching_template: Option<String>,
    pub worst_matching_template: Option<String>,
}

/// 片段匹配结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SegmentMatch {
    pub track_segment_id: String,
    pub track_segment_name: String,
    pub material_segment_id: String,
    pub material_segment: MaterialSegment,
    pub material_name: String,
    pub model_name: Option<String>,
    pub match_score: f64,
    pub match_reason: String,
}

/// 匹配失败的片段
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FailedSegmentMatch {
    pub track_segment_id: String,
    pub track_segment_name: String,
    pub matching_rule: SegmentMatchingRule,
    pub failure_reason: String,
}

/// 匹配统计信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatchingStatistics {
    pub total_segments: u32,
    pub matched_segments: u32,
    pub failed_segments: u32,
    pub success_rate: f64,
    pub used_materials: u32,
    pub used_models: u32,
}

impl MaterialMatchingService {
    /// 创建新的素材匹配服务实例
    pub fn new(
        material_repo: Arc<MaterialRepository>,
        material_usage_repo: Arc<MaterialUsageRepository>,
        template_service: Arc<TemplateService>,
        video_classification_repo: Arc<VideoClassificationRepository>,
        ai_classification_service: Arc<crate::business::services::ai_classification_service::AiClassificationService>,
    ) -> Self {
        Self {
            material_repo,
            material_usage_repo,
            template_service,
            video_classification_repo,
            ai_classification_service,
            template_segment_weight_service: None,
            matching_result_service: None,
            event_bus: Arc::new(EventBusManager::new()),
        }
    }

    /// 创建新的素材匹配服务实例（带结果保存功能）
    pub fn new_with_result_service(
        material_repo: Arc<MaterialRepository>,
        material_usage_repo: Arc<MaterialUsageRepository>,
        template_service: Arc<TemplateService>,
        video_classification_repo: Arc<VideoClassificationRepository>,
        ai_classification_service: Arc<crate::business::services::ai_classification_service::AiClassificationService>,
        matching_result_service: Arc<TemplateMatchingResultService>,
    ) -> Self {
        Self {
            material_repo,
            material_usage_repo,
            template_service,
            video_classification_repo,
            ai_classification_service,
            template_segment_weight_service: None,
            matching_result_service: Some(matching_result_service),
            event_bus: Arc::new(EventBusManager::new()),
        }
    }

    /// 设置模板片段权重服务
    pub fn set_template_segment_weight_service(&mut self, service: Arc<TemplateSegmentWeightService>) {
        self.template_segment_weight_service = Some(service);
    }

    /// 执行素材匹配
    pub async fn match_materials(&self, request: MaterialMatchingRequest) -> Result<MaterialMatchingResult> {
        // 获取模板信息
        let template = self.template_service.get_template_by_id(&request.template_id)
            .await?
            .ok_or_else(|| anyhow!("模板不存在: {}", request.template_id))?;

        // 获取项目的所有素材（包含片段信息）
        let project_materials = crate::business::services::material_service::MaterialService::get_project_materials(
            &self.material_repo,
            &request.project_id
        )?;

        // 获取所有素材的分类记录
        let mut classification_records = HashMap::new();
        for material in &project_materials {
            let records = self.video_classification_repo.get_by_material_id(&material.id).await?;
            classification_records.insert(material.id.clone(), records);
        }

        // 获取所有可用的素材片段（已分类的，排除已使用的）
        let available_segments = self.get_classified_segments(&project_materials, &classification_records, &request.project_id).await?;



        // 执行匹配算法
        let mut matches = Vec::new();
        let mut failed_segments = Vec::new();
        let mut fixed_segments = Vec::new(); // 新增：固定素材片段统计
        let mut used_segment_ids = HashSet::new();
        let mut used_model_ids = HashSet::new();

        // 获取所有需要匹配的轨道片段
        let track_segments = self.get_template_track_segments(&template).await?;



        for track_segment in &track_segments {
            // 检查是否为固定素材
            if track_segment.matching_rule.is_fixed_material() {
                fixed_segments.push(track_segment.clone());
                continue; // 固定素材跳过匹配，不计入失败
            }

            match self.match_single_segment(
                track_segment,
                &available_segments,
                &classification_records,
                &project_materials,
                &mut used_segment_ids,
            ).await {
                Ok(segment_match) => {
                    // 记录使用的模特
                    if let Some(material) = project_materials.iter().find(|m| m.id == segment_match.material_segment.material_id) {
                        if let Some(model_id) = &material.model_id {
                            used_model_ids.insert(model_id.clone());
                        }
                    }
                    matches.push(segment_match);
                }
                Err(failure_reason) => {
                    failed_segments.push(FailedSegmentMatch {
                        track_segment_id: track_segment.id.clone(),
                        track_segment_name: track_segment.name.clone(),
                        matching_rule: track_segment.matching_rule.clone(),
                        failure_reason,
                    });
                }
            }
        }

        // 计算统计信息
        let total_segments = track_segments.len() as u32;
        let fixed_segments_count = fixed_segments.len() as u32;
        let matched_segments = matches.len() as u32;
        let failed_segments_count = failed_segments.len() as u32;

        // 正确的成功率计算：(成功匹配的片段 + 固定片段) / 总片段数
        let successful_segments = matched_segments + fixed_segments_count;
        let success_rate = if total_segments > 0 {
            successful_segments as f64 / total_segments as f64
        } else {
            1.0 // 如果没有片段，成功率为100%
        };

        let statistics = MatchingStatistics {
            total_segments, // 统计所有片段
            matched_segments,
            failed_segments: failed_segments_count,
            success_rate,
            used_materials: used_segment_ids.len() as u32,
            used_models: used_model_ids.len() as u32,
        };

        Ok(MaterialMatchingResult {
            binding_id: request.binding_id,
            template_id: request.template_id,
            project_id: request.project_id,
            matches,
            statistics,
            failed_segments,
        })
    }

    /// 执行素材匹配并自动保存结果到数据库
    pub async fn match_materials_and_save(
        &self,
        request: MaterialMatchingRequest,
        result_name: String,
        description: Option<String>,
    ) -> Result<(MaterialMatchingResult, Option<crate::data::models::template_matching_result::TemplateMatchingResult>)> {
        let start_time = std::time::Instant::now();

        // 执行匹配
        let matching_result = self.match_materials(request).await?;

        let matching_duration_ms = start_time.elapsed().as_millis() as u64;

        // 判断匹配是否成功：没有失败的片段
        let is_matching_successful = matching_result.failed_segments.is_empty();

        // 只有匹配完全成功时才保存到数据库
        if is_matching_successful {
            // 如果配置了结果保存服务，则自动保存结果
            if let Some(result_service) = &self.matching_result_service {
                match result_service.save_matching_result(
                    &matching_result,
                    result_name,
                    description,
                    matching_duration_ms,
                ).await {
                    Ok(saved_result) => {
                        // 创建素材使用记录
                        let usage_requests: Vec<crate::data::models::material_usage::CreateMaterialUsageRecordRequest> = matching_result.matches
                            .iter()
                            .map(|segment_match| {
                                let usage_context = serde_json::json!({
                                    "match_score": segment_match.match_score,
                                    "match_reason": segment_match.match_reason,
                                    "material_name": segment_match.material_name,
                                    "model_name": segment_match.model_name
                                }).to_string();

                                crate::data::models::material_usage::CreateMaterialUsageRecordRequest {
                                    material_segment_id: segment_match.material_segment_id.clone(),
                                    material_id: segment_match.material_segment.material_id.clone(),
                                    project_id: matching_result.project_id.clone(),
                                    template_matching_result_id: saved_result.id.clone(),
                                    template_id: matching_result.template_id.clone(),
                                    binding_id: matching_result.binding_id.clone(),
                                    track_segment_id: segment_match.track_segment_id.clone(),
                                    usage_type: crate::data::models::material_usage::MaterialUsageType::TemplateMatching,
                                    usage_context: Some(usage_context),
                                }
                            })
                            .collect();

                        if !usage_requests.is_empty() {
                            match self.material_usage_repo.create_usage_records_batch(usage_requests) {
                                Ok(_usage_records) => {
                                    // 素材使用记录创建成功
                                }
                                Err(_e) => {
                                    // 创建素材使用记录失败，但不阻断主流程
                                }
                            }
                        }

                        return Ok((matching_result, Some(saved_result)));
                    }
                    Err(_e) => {
                        // 保存失败时不影响匹配结果的返回
                    }
                }
            }
        } else {
            // 匹配失败，不保存到数据库
        }

        Ok((matching_result, None))
    }

    /// 获取已分类的素材片段（排除已使用的片段）
    async fn get_classified_segments(
        &self,
        materials: &[Material],
        classification_records: &HashMap<String, Vec<VideoClassificationRecord>>,
        project_id: &str,
    ) -> Result<Vec<(MaterialSegment, String)>> {
        self.get_classified_segments_with_exclusions(materials, classification_records, project_id, &HashSet::new()).await
    }

    /// 获取已分类的素材片段（排除已使用的片段和额外排除的片段）
    async fn get_classified_segments_with_exclusions(
        &self,
        materials: &[Material],
        classification_records: &HashMap<String, Vec<VideoClassificationRecord>>,
        project_id: &str,
        additional_used_segments: &HashSet<String>,
    ) -> Result<Vec<(MaterialSegment, String)>> {
        let mut classified_segments = Vec::new();

        // 获取项目中已使用的素材片段ID列表（从数据库）
        let mut used_segment_ids = match self.material_usage_repo.get_usage_records_by_project(project_id) {
            Ok(usage_records) => {
                usage_records.into_iter()
                    .map(|record| record.material_segment_id)
                    .collect::<HashSet<String>>()
            }
            Err(_e) => {
                // 如果获取使用记录失败，不影响匹配流程
                HashSet::new()
            }
        };

        // 合并额外的已使用片段ID
        used_segment_ids.extend(additional_used_segments.iter().cloned());

        for material in materials {

            // 只处理有分类记录的素材
            if let Some(records) = classification_records.get(&material.id) {
                if records.is_empty() {
                    continue;
                }

                // 检查素材和分类记录

                // 检查是否有素材片段
                if material.segments.is_empty() {
                    // 如果素材没有被切分，但有分类记录，我们需要为每个分类记录创建对应的虚拟片段
                    // 因为每个分类记录的segment_id对应一个具体的片段
                    if let Some(duration) = material.get_duration() {
                        // 为每个分类记录创建一个虚拟片段
                        for record in records {
                            // 检查该片段是否已被使用
                            if used_segment_ids.contains(&record.segment_id) {
                                continue; // 跳过已使用的片段
                            }

                            // 创建虚拟片段

                            // 创建虚拟片段，使用分类记录中的segment_id
                            let virtual_segment = MaterialSegment {
                                id: record.segment_id.clone(), // 使用分类记录中的segment_id
                                material_id: material.id.clone(),
                                segment_index: 0,
                                start_time: 0.0,
                                end_time: duration,
                                duration,
                                file_path: material.original_path.clone(), // 使用原始文件路径
                                file_size: material.file_size,
                                thumbnail_path: material.thumbnail_path.clone(),
                                usage_count: 0,
                                is_used: false,
                                last_used_at: None,
                                created_at: chrono::Utc::now(),
                            };

                            classified_segments.push((virtual_segment, record.category.clone()));
                        }
                    }
                } else {
                    // 为每个素材片段查找对应的分类记录
                    for segment in &material.segments {
                        // 检查该片段是否已被使用
                        if used_segment_ids.contains(&segment.id) {
                            continue; // 跳过已使用的片段
                        }

                        // 查找该片段的分类记录
                        if let Some(record) = records.iter().find(|r| r.segment_id == segment.id) {
                            classified_segments.push((segment.clone(), record.category.clone()));
                        }
                    }
                }
            }
        }


        Ok(classified_segments)
    }

    /// 获取模板的所有轨道片段
    async fn get_template_track_segments(&self, template: &Template) -> Result<Vec<TrackSegment>> {
        let mut all_segments = Vec::new();

        for track in &template.tracks {
            all_segments.extend(track.segments.clone());
        }

        Ok(all_segments)
    }

    /// 匹配单个轨道片段
    /// 注意：此方法不应该被固定素材调用，固定素材在上层已被过滤
    async fn match_single_segment(
        &self,
        track_segment: &TrackSegment,
        available_segments: &[(MaterialSegment, String)],
        _classification_records: &HashMap<String, Vec<VideoClassificationRecord>>,
        project_materials: &[Material],
        used_segment_ids: &mut HashSet<String>,
    ) -> Result<SegmentMatch, String> {
        // 全局限制：检查当前模板匹配过程中是否已经使用了以001结尾的文件
        let mut template_already_used_sequence_001 = false;
        for used_id in used_segment_ids.iter() {
            if let Some((used_segment, _)) = available_segments.iter().find(|(seg, _)| seg.id == *used_id) {
                if FilenameUtils::has_sequence_001(&used_segment.file_path) && FilenameUtils::is_video_file(&used_segment.file_path) {
                    template_already_used_sequence_001 = true;
                    break;
                }
            }
        }

        // 检查匹配规则
        match &track_segment.matching_rule {
            SegmentMatchingRule::FixedMaterial => {
                // 这种情况不应该发生，因为固定素材在上层已被过滤
                Err("固定素材不应该调用此方法".to_string())
            }
            SegmentMatchingRule::AiClassification { category_name, .. } => {
                self.match_by_ai_classification(
                    track_segment,
                    available_segments,
                    category_name,
                    project_materials,
                    used_segment_ids,
                    template_already_used_sequence_001,
                ).await
            }
            SegmentMatchingRule::RandomMatch => {
                self.match_randomly(
                    track_segment,
                    available_segments,
                    project_materials,
                    used_segment_ids,
                    template_already_used_sequence_001,
                ).await
            }
            SegmentMatchingRule::FilenameSequence { target_sequence } => {
                self.match_by_filename_sequence(
                    track_segment,
                    available_segments,
                    target_sequence,
                    project_materials,
                    used_segment_ids,
                    template_already_used_sequence_001,
                ).await
            }
            SegmentMatchingRule::PriorityOrder { category_ids } => {
                // 如果有模板片段权重服务，使用模板级权重，否则使用全局权重
                if let Some(weight_service) = &self.template_segment_weight_service {
                    self.match_by_template_segment_priority_order(
                        track_segment,
                        available_segments,
                        category_ids,
                        project_materials,
                        used_segment_ids,
                        template_already_used_sequence_001,
                        weight_service,
                    ).await
                } else {
                    self.match_by_priority_order(
                        track_segment,
                        available_segments,
                        category_ids,
                        project_materials,
                        used_segment_ids,
                        template_already_used_sequence_001,
                    ).await
                }
            }
        }
    }

    /// 根据AI分类匹配素材
    async fn match_by_ai_classification(
        &self,
        track_segment: &TrackSegment,
        available_segments: &[(MaterialSegment, String)],
        target_category: &str,
        project_materials: &[Material],
        used_segment_ids: &mut HashSet<String>,
        template_already_used_sequence_001: bool,
    ) -> Result<SegmentMatch, String> {
        // 计算目标时长（微秒转秒）- 修复单位转换
        let target_duration = track_segment.duration as f64 / 1_000_000.0;

        // 过滤出匹配分类的片段，同时应用序号001限制
        let category_segments: Vec<_> = available_segments
            .iter()
            .filter(|(segment, category)| {
                // 基本条件：分类匹配且未被使用
                if category != target_category || used_segment_ids.contains(&segment.id) {
                    return false;
                }

                // 全局限制：如果模板已经使用了序号001的视频，则跳过所有序号001的视频
                if template_already_used_sequence_001
                    && FilenameUtils::has_sequence_001(&segment.file_path)
                    && FilenameUtils::is_video_file(&segment.file_path) {
                    return false;
                }

                true
            })
            .collect();

        if category_segments.is_empty() {
            // 提供更详细的错误信息
            let all_categories: Vec<String> = available_segments
                .iter()
                .map(|(_, category)| category.clone())
                .collect::<std::collections::HashSet<_>>()
                .into_iter()
                .collect();
            return Err(format!(
                "没有找到分类为'{}'的可用素材片段。可用分类: [{}]",
                target_category,
                all_categories.join(", ")
            ));
        }

        // 按模特分组
        let mut model_groups: HashMap<Option<String>, Vec<_>> = HashMap::new();
        for (segment, category) in category_segments {
            if let Some(material) = project_materials.iter().find(|m| m.id == segment.material_id) {
                let model_id = material.model_id.clone();
                model_groups.entry(model_id).or_default().push((segment, category, material));
            }
        }

        // 尝试每个模特的素材
        for (model_id, model_segments) in model_groups {
            if let Some(best_match) = self.find_best_duration_match(
                &model_segments,
                target_duration,
            ) {
                let (segment, _category, material) = best_match;

                // 标记为已使用
                used_segment_ids.insert(segment.id.clone());

                return Ok(SegmentMatch {
                    track_segment_id: track_segment.id.clone(),
                    track_segment_name: track_segment.name.clone(),
                    material_segment_id: segment.id.clone(),
                    material_segment: (*segment).clone(),
                    material_name: material.name.clone(),
                    model_name: model_id.clone(),
                    match_score: segment.duration_match_score(target_duration),
                    match_reason: format!("AI分类匹配: {}", target_category),
                });
            }
        }

        Err(format!("没有找到满足时长要求的分类为'{}'的素材片段", target_category))
    }

    /// 随机匹配素材
    async fn match_randomly(
        &self,
        track_segment: &TrackSegment,
        available_segments: &[(MaterialSegment, String)],
        project_materials: &[Material],
        used_segment_ids: &mut HashSet<String>,
        template_already_used_sequence_001: bool,
    ) -> Result<SegmentMatch, String> {
        // 计算目标时长（微秒转秒）- 修复单位转换
        let target_duration = track_segment.duration as f64 / 1_000_000.0;

        // 过滤出未使用的片段，同时应用序号001限制
        let unused_segments: Vec<_> = available_segments
            .iter()
            .filter(|(segment, _)| {
                // 基本条件：未被使用
                if used_segment_ids.contains(&segment.id) {
                    return false;
                }

                // 全局限制：如果模板已经使用了序号001的视频，则跳过所有序号001的视频
                if template_already_used_sequence_001
                    && FilenameUtils::has_sequence_001(&segment.file_path)
                    && FilenameUtils::is_video_file(&segment.file_path) {
                    return false;
                }

                true
            })
            .collect();

        if unused_segments.is_empty() {
            return Err("没有可用的素材片段进行随机匹配".to_string());
        }

        // 按模特分组
        let mut model_groups: HashMap<Option<String>, Vec<_>> = HashMap::new();
        for (segment, category) in unused_segments {
            if let Some(material) = project_materials.iter().find(|m| m.id == segment.material_id) {
                let model_id = material.model_id.clone();
                model_groups.entry(model_id).or_default().push((segment, category, material));
            }
        }

        // 尝试每个模特的素材
        for (model_id, model_segments) in model_groups {
            if let Some(best_match) = self.find_best_duration_match(
                &model_segments,
                target_duration,
            ) {
                let (segment, category, material) = best_match;

                // 标记为已使用
                used_segment_ids.insert(segment.id.clone());

                return Ok(SegmentMatch {
                    track_segment_id: track_segment.id.clone(),
                    track_segment_name: track_segment.name.clone(),
                    material_segment_id: segment.id.clone(),
                    material_segment: (*segment).clone(),
                    material_name: material.name.clone(),
                    model_name: model_id.clone(),
                    match_score: segment.duration_match_score(target_duration),
                    match_reason: format!("随机匹配: {}", category),
                });
            }
        }

        Err("没有找到满足时长要求的素材片段进行随机匹配".to_string())
    }

    /// 在给定的片段中找到最佳时长匹配
    fn find_best_duration_match<'a>(
        &self,
        segments: &'a [(&MaterialSegment, &String, &Material)],
        target_duration: f64,
    ) -> Option<(&'a MaterialSegment, &'a String, &'a Material)> {
        let mut best_match = None;
        let mut best_score = 0.0;

        for (segment, category, material) in segments.iter() {
            let meets_requirement = segment.meets_duration_requirement(target_duration);
            let score = segment.duration_match_score(target_duration);

            if meets_requirement && score > best_score {
                best_score = score;
                best_match = Some((*segment, *category, *material));
            }
        }

        best_match
    }

    /// 根据文件名序号匹配素材
    async fn match_by_filename_sequence(
        &self,
        track_segment: &TrackSegment,
        available_segments: &[(MaterialSegment, String)],
        target_sequence: &str,
        project_materials: &[Material],
        used_segment_ids: &mut HashSet<String>,
        template_already_used_sequence_001: bool,
    ) -> Result<SegmentMatch, String> {
        let target_duration = track_segment.duration as f64 / 1_000_000.0; // 转换为秒

        // 序号001限制检查已在上层完成，这里直接使用传递的参数

        // 筛选出包含目标序号的视频文件片段
        let mut matching_segments = Vec::new();

        for (segment, category) in available_segments {
            // 跳过已使用的片段
            if used_segment_ids.contains(&segment.id) {
                continue;
            }

            // 检查文件名是否包含目标序号
            if FilenameUtils::has_sequence_number(&segment.file_path, target_sequence) {
                // 全局限制：如果模板已经使用了序号001的视频，则跳过所有序号001的视频
                if template_already_used_sequence_001
                    && FilenameUtils::has_sequence_001(&segment.file_path)
                    && FilenameUtils::is_video_file(&segment.file_path) {
                    continue;
                }

                // 确保是视频文件
                if FilenameUtils::is_video_file(&segment.file_path) {
                    // 查找对应的素材信息
                    if let Some(material) = project_materials.iter().find(|m| m.id == segment.material_id) {
                        matching_segments.push((segment, category, material));
                    }
                }
            }
        }

        if matching_segments.is_empty() {
            return Err(format!("没有找到包含序号 {} 的视频文件", target_sequence));
        }

        // 检查是否找到匹配的片段

        // 在匹配的片段中找到最佳时长匹配
        if let Some((best_segment, _best_category, best_material)) = self.find_best_duration_match(
            &matching_segments,
            target_duration,
        ) {
            // 标记为已使用
            used_segment_ids.insert(best_segment.id.clone());

            return Ok(SegmentMatch {
                track_segment_id: track_segment.id.clone(),
                track_segment_name: track_segment.name.clone(),
                material_segment_id: best_segment.id.clone(),
                material_segment: (*best_segment).clone(),
                material_name: best_material.name.clone(),
                model_name: best_material.model_id.clone(),
                match_score: best_segment.duration_match_score(target_duration),
                match_reason: format!("文件名序号匹配: {}", target_sequence),
            });
        }

        Err(format!("没有找到满足时长要求且包含序号 {} 的视频片段", target_sequence))
    }

    /// 按优先级顺序匹配素材
    async fn match_by_priority_order(
        &self,
        track_segment: &TrackSegment,
        available_segments: &[(MaterialSegment, String)],
        category_ids: &[String],
        project_materials: &[Material],
        used_segment_ids: &mut HashSet<String>,
        template_already_used_sequence_001: bool,
    ) -> Result<SegmentMatch, String> {
        let _target_duration = track_segment.duration as f64 / 1_000_000.0; // 转换为秒

        // 获取所有AI分类，按权重排序
        let ai_classifications = match self.ai_classification_service.get_classifications_by_weight().await {
            Ok(classifications) => classifications,
            Err(_) => {
                return Err("无法获取AI分类信息".to_string());
            }
        };

        // 按权重顺序尝试匹配每个分类
        for classification in ai_classifications {
            // 检查当前分类是否在指定的分类列表中
            if !category_ids.contains(&classification.id) {
                continue;
            }

            // 尝试匹配当前分类的素材
            let matching_result = self.match_by_ai_classification(
                track_segment,
                available_segments,
                &classification.name,
                project_materials,
                used_segment_ids,
                template_already_used_sequence_001,
            ).await;

            // 如果匹配成功，返回结果
            if let Ok(segment_match) = matching_result {
                return Ok(SegmentMatch {
                    track_segment_id: segment_match.track_segment_id,
                    track_segment_name: segment_match.track_segment_name,
                    material_segment_id: segment_match.material_segment_id,
                    material_segment: segment_match.material_segment,
                    material_name: segment_match.material_name,
                    model_name: segment_match.model_name,
                    match_score: segment_match.match_score,
                    match_reason: format!("按顺序匹配: {} (权重: {})", classification.name, classification.weight),
                });
            }
        }

        Err("按优先级顺序匹配失败：没有找到合适的素材".to_string())
    }

    /// 执行一键匹配 - 遍历项目的所有活跃模板绑定并逐一匹配
    pub async fn batch_match_all_templates(&self, request: BatchMatchingRequest, database: Arc<crate::infrastructure::database::Database>) -> Result<BatchMatchingResult> {
        // 调用优化的循环匹配方法
        self.batch_match_all_templates_optimized(request, database, None).await
    }

    /// 执行一键匹配（带事件发送）
    pub async fn batch_match_all_templates_with_events(&self, request: BatchMatchingRequest, database: Arc<crate::infrastructure::database::Database>, app_handle: Option<tauri::AppHandle>) -> Result<BatchMatchingResult> {
        // 调用优化的循环匹配方法
        self.batch_match_all_templates_optimized(request, database, app_handle).await
    }

    /// 优化的一键匹配 - 循环匹配模板直到失败（无法完整匹配模板 -- 素材不够用）
    pub async fn batch_match_all_templates_optimized(&self, request: BatchMatchingRequest, database: Arc<crate::infrastructure::database::Database>, app_handle: Option<tauri::AppHandle>) -> Result<BatchMatchingResult> {
        let start_time = std::time::Instant::now();

        // 获取项目的所有活跃模板绑定
        let active_bindings = self.get_active_project_bindings(&request.project_id, database).await?;

        if active_bindings.is_empty() {
            return Ok(BatchMatchingResult {
                project_id: request.project_id,
                total_bindings: 0,
                successful_matches: 0,
                failed_matches: 0,
                skipped_bindings: 0,
                matching_results: Vec::new(),
                total_duration_ms: start_time.elapsed().as_millis() as u64,
                summary: BatchMatchingSummary {
                    total_segments_matched: 0,
                    total_materials_used: 0,
                    total_models_used: 0,
                    average_success_rate: 0.0,
                    best_matching_template: None,
                    worst_matching_template: None,
                },
                total_rounds: 0,
                successful_rounds: 0,
                termination_reason: "没有活跃的模板绑定".to_string(),
                materials_exhausted: false,
            });
        }

        // 初始化循环匹配状态
        let mut matching_results = Vec::new();
        let mut successful_matches = 0u32;
        let mut failed_matches = 0u32;
        let skipped_bindings = 0u32;
        let mut total_rounds = 0u32;
        let mut successful_rounds = 0u32;
        let mut global_used_segment_ids = HashSet::new();
        let mut termination_reason: Option<String> = None;
        let mut materials_exhausted = false;

        // 为每个模板维护独立的序号计数器
        let mut template_counters: HashMap<String, u32> = HashMap::new();

        // 跟踪哪些模板已经使用了序号001的视频（确保每个模板只能使用一个序号001的视频）
        // 注意：这个跟踪在单个模板匹配过程中进行，不需要全局恢复

        // 获取项目中已使用的素材片段ID列表（从数据库）
        let existing_used_segments = match self.material_usage_repo.get_usage_records_by_project(&request.project_id) {
            Ok(usage_records) => {
                usage_records.into_iter()
                    .map(|record| record.material_segment_id)
                    .collect::<HashSet<String>>()
            }
            Err(_e) => {
                HashSet::new()
            }
        };
        global_used_segment_ids.extend(existing_used_segments);

        // 开始循环匹配
        loop {
            total_rounds += 1;
            let _round_start_time = std::time::Instant::now();
            let mut round_successful_matches = 0u32;
            let mut _round_failed_matches = 0u32;

            // 记录本轮开始时的已使用片段数量，用于检测是否有实质性进展
            let segments_count_before_round = global_used_segment_ids.len();

            // 性能优化：预检查是否有任何模板可以匹配（每5轮检查一次以避免过度检查）
            if total_rounds % 5 == 1 && total_rounds > 1 {
                let mut any_template_can_match = false;
                for binding_detail in &active_bindings {
                    let can_match = self.can_template_be_fully_matched(
                        &binding_detail.binding.template_id,
                        &request.project_id,
                        &global_used_segment_ids,
                    ).await;

                    if can_match {
                        any_template_can_match = true;
                        break;
                    }
                }

                // 如果没有任何模板可以匹配，提前终止
                if !any_template_can_match {
                    termination_reason = Some("预检查发现无任何模板可完整匹配，提前终止".to_string());
                    materials_exhausted = true;
                    break;
                }
            }

            // 逐一尝试匹配每个模板绑定
            for (binding_index, binding_detail) in active_bindings.iter().enumerate() {
                let binding_start_time = std::time::Instant::now();

                // 发送进度事件
                if let Some(ref handle) = app_handle {
                    let current_binding_index = (total_rounds - 1) as usize * active_bindings.len() + binding_index + 1;
                    let _ = handle.emit("batch_matching_progress", serde_json::json!({
                        "project_id": request.project_id,
                        "current_binding_index": current_binding_index,
                        "total_bindings": active_bindings.len() * 100, // 估算总数
                        "current_template_name": binding_detail.template_name,
                        "completed_bindings": successful_matches,
                        "failed_bindings": failed_matches,
                        "elapsed_time_ms": start_time.elapsed().as_millis() as u64,
                    }));
                }

                let matching_request = MaterialMatchingRequest {
                    project_id: request.project_id.clone(),
                    template_id: binding_detail.binding.template_id.clone(),
                    binding_id: binding_detail.binding.id.clone(),
                    overwrite_existing: request.overwrite_existing,
                };

                // 改进的命名逻辑：模板名称 + 序号（每个模板独立计数）
                let template_counter = template_counters.entry(binding_detail.template_name.clone()).or_insert(0);
                *template_counter += 1;

                let result_name = if let Some(prefix) = &request.result_name_prefix {
                    format!("{}-{}-{:03}", prefix, binding_detail.template_name, *template_counter)
                } else {
                    format!("{}-{:03}", binding_detail.template_name, *template_counter)
                };

                match self.match_materials_with_used_segments(matching_request, result_name, &global_used_segment_ids).await {
                    Ok((matching_result, saved_result, newly_used_segments)) => {
                        // 检查匹配是否完全成功（没有失败的片段）
                        let is_fully_successful = matching_result.failed_segments.is_empty();

                        if is_fully_successful {
                            round_successful_matches += 1;
                            successful_matches += 1;

                            // 更新全局已使用片段列表
                            global_used_segment_ids.extend(newly_used_segments);

                            matching_results.push(BatchMatchingItemResult {
                                binding_id: binding_detail.binding.id.clone(),
                                template_id: binding_detail.binding.template_id.clone(),
                                template_name: binding_detail.template_name.clone(),
                                binding_name: binding_detail.binding.binding_name.clone(),
                                status: BatchMatchingItemStatus::Success,
                                matching_result: Some(matching_result),
                                saved_result_id: saved_result.map(|r| r.id),
                                error_message: None,
                                duration_ms: binding_start_time.elapsed().as_millis() as u64,
                                round_number: total_rounds,
                                attempts_count: 1,
                                failure_reason: None,
                            });
                        } else {
                            // 部分匹配失败，视为失败
                            _round_failed_matches += 1;
                            failed_matches += 1;

                            let failure_reason = format!("部分匹配失败：{} 个片段匹配成功，{} 个片段匹配失败",
                                matching_result.matches.len(),
                                matching_result.failed_segments.len()
                            );

                            matching_results.push(BatchMatchingItemResult {
                                binding_id: binding_detail.binding.id.clone(),
                                template_id: binding_detail.binding.template_id.clone(),
                                template_name: binding_detail.template_name.clone(),
                                binding_name: binding_detail.binding.binding_name.clone(),
                                status: BatchMatchingItemStatus::Failed,
                                matching_result: Some(matching_result), // 仍然返回结果用于分析
                                saved_result_id: None, // 但不保存到数据库
                                error_message: Some(failure_reason.clone()),
                                duration_ms: binding_start_time.elapsed().as_millis() as u64,
                                round_number: total_rounds,
                                attempts_count: 1,
                                failure_reason: Some(failure_reason),
                            });
                        }
                    }
                    Err(error) => {
                        _round_failed_matches += 1;
                        failed_matches += 1;

                        matching_results.push(BatchMatchingItemResult {
                            binding_id: binding_detail.binding.id.clone(),
                            template_id: binding_detail.binding.template_id.clone(),
                            template_name: binding_detail.template_name.clone(),
                            binding_name: binding_detail.binding.binding_name.clone(),
                            status: BatchMatchingItemStatus::Failed,
                            matching_result: None,
                            saved_result_id: None,
                            error_message: Some(error.to_string()),
                            duration_ms: binding_start_time.elapsed().as_millis() as u64,
                            round_number: total_rounds,
                            attempts_count: 1,
                            failure_reason: Some(error.to_string()),
                        });
                    }
                }
            }

            // 检查本轮是否有实质性进展（新增了已使用的片段）
            let segments_count_after_round = global_used_segment_ids.len();
            let has_substantial_progress = segments_count_after_round > segments_count_before_round;

            // 检查是否应该继续下一轮 - 增强的终止条件
            if round_successful_matches == 0 {
                // 本轮没有任何成功匹配，终止循环
                termination_reason = Some(format!("第{}轮无任何成功匹配，素材已耗尽", total_rounds));
                materials_exhausted = true;
                break;
            } else if !has_substantial_progress {
                // 本轮虽然有成功匹配，但没有实质性进展（没有新增已使用片段），可能陷入无效循环
                termination_reason = Some(format!("第{}轮无实质性进展，可能素材不足以完整匹配", total_rounds));
                materials_exhausted = true;
                break;
            } else {
                successful_rounds += 1;
            }

            // 可选：添加最大轮数限制，防止无限循环
            if total_rounds >= 100 {
                termination_reason = Some("达到最大轮数限制(100轮)".to_string());
                break;
            }
        }

        // 设置默认终止原因（如果没有设置）
        let final_termination_reason = termination_reason.unwrap_or_else(|| "正常完成".to_string());

        // 计算汇总信息
        let summary = self.calculate_batch_summary(&matching_results);

        Ok(BatchMatchingResult {
            project_id: request.project_id,
            total_bindings: active_bindings.len() as u32,
            successful_matches,
            failed_matches,
            skipped_bindings,
            matching_results,
            total_duration_ms: start_time.elapsed().as_millis() as u64,
            summary,
            total_rounds,
            successful_rounds,
            termination_reason: final_termination_reason,
            materials_exhausted,
        })
    }

    /// 获取项目的活跃模板绑定
    async fn get_active_project_bindings(&self, project_id: &str, database: Arc<crate::infrastructure::database::Database>) -> Result<Vec<crate::data::models::project_template_binding::ProjectTemplateBindingDetail>> {
        use crate::data::repositories::project_template_binding_repository::ProjectTemplateBindingRepository;
        use crate::data::models::project_template_binding::BindingStatus;

        let binding_repo = ProjectTemplateBindingRepository::new(database);

        // 查询活跃的绑定
        let bindings = binding_repo.get_templates_by_project(project_id)?;

        // 过滤出活跃的绑定
        let active_bindings: Vec<_> = bindings.into_iter()
            .filter(|detail| detail.binding.is_active && detail.binding.binding_status == BindingStatus::Active)
            .collect();

        Ok(active_bindings)
    }

    /// 检查模板是否可以完整匹配（考虑当前已使用的素材）
    async fn can_template_be_fully_matched(
        &self,
        template_id: &str,
        project_id: &str,
        used_segment_ids: &HashSet<String>,
    ) -> bool {
        // 获取模板信息
        let template = match self.template_service.get_template_by_id(template_id).await {
            Ok(Some(template)) => template,
            _ => return false,
        };

        // 获取项目的所有素材（包含片段信息）
        let project_materials = match crate::business::services::material_service::MaterialService::get_project_materials(
            &self.material_repo,
            project_id
        ) {
            Ok(materials) => materials,
            Err(_) => return false,
        };

        // 获取所有素材的分类记录
        let mut classification_records = HashMap::new();
        for material in &project_materials {
            if let Ok(records) = self.video_classification_repo.get_by_material_id(&material.id).await {
                classification_records.insert(material.id.clone(), records);
            }
        }

        // 获取可用的素材片段（排除已使用的）
        let available_segments = match self.get_classified_segments_with_exclusions(
            &project_materials,
            &classification_records,
            project_id,
            used_segment_ids
        ).await {
            Ok(segments) => segments,
            Err(_) => return false,
        };

        // 检查每个非固定素材片段是否都能找到匹配
        for track in &template.tracks {
            for segment in &track.segments {
                if segment.matching_rule.is_fixed_material() {
                    continue; // 固定素材跳过
                }

                // 检查是否有可用的素材片段可以匹配此轨道片段
                let can_match = match &segment.matching_rule {
                    SegmentMatchingRule::AiClassification { category_name, .. } => {
                        available_segments.iter().any(|(_, category)| category == category_name)
                    }
                    SegmentMatchingRule::RandomMatch => {
                        !available_segments.is_empty()
                    }
                    SegmentMatchingRule::FilenameSequence { target_sequence } => {
                        // 检查是否有包含目标序号的视频文件
                        available_segments.iter().any(|(segment, _)| {
                            FilenameUtils::is_video_file(&segment.file_path) &&
                            FilenameUtils::has_sequence_number(&segment.file_path, target_sequence)
                        })
                    }
                    SegmentMatchingRule::PriorityOrder { category_ids } => {
                        // 检查是否有任何指定分类的素材可用
                        category_ids.iter().any(|category_id| {
                            available_segments.iter().any(|(_, category)| {
                                // 这里需要通过category_id查找category_name进行匹配
                                // 暂时使用简单的字符串匹配，后续可以优化
                                category.contains(category_id)
                            })
                        })
                    }
                    _ => false,
                };

                if !can_match {
                    return false; // 有片段无法匹配
                }
            }
        }

        true
    }



    /// 使用指定的已使用素材列表进行匹配
    async fn match_materials_with_used_segments(
        &self,
        request: MaterialMatchingRequest,
        result_name: String,
        used_segment_ids: &HashSet<String>,
    ) -> Result<(MaterialMatchingResult, Option<crate::data::models::template_matching_result::TemplateMatchingResult>, HashSet<String>)> {
        // 获取模板信息
        let template = self.template_service.get_template_by_id(&request.template_id)
            .await?
            .ok_or_else(|| anyhow!("模板不存在: {}", request.template_id))?;

        // 获取项目的所有素材（包含片段信息）
        let project_materials = crate::business::services::material_service::MaterialService::get_project_materials(
            &self.material_repo,
            &request.project_id
        )?;

        // 获取所有素材的分类记录
        let mut classification_records = HashMap::new();
        for material in &project_materials {
            let records = self.video_classification_repo.get_by_material_id(&material.id).await?;
            classification_records.insert(material.id.clone(), records);
        }

        // 获取可用的素材片段（排除已使用的片段）
        let available_segments = self.get_classified_segments_with_exclusions(
            &project_materials,
            &classification_records,
            &request.project_id,
            used_segment_ids
        ).await?;

        // 执行匹配算法
        let mut matches = Vec::new();
        let mut failed_segments = Vec::new();
        let mut fixed_segments = Vec::new();
        let mut local_used_segment_ids = HashSet::new();
        let mut used_model_ids = HashSet::new();

        // 获取所有需要匹配的轨道片段
        let track_segments = self.get_template_track_segments(&template).await?;

        for track_segment in &track_segments {
            // 检查是否为固定素材
            if track_segment.matching_rule.is_fixed_material() {
                fixed_segments.push(track_segment.clone());
                continue; // 固定素材跳过匹配，不计入失败
            }

            // 尝试匹配片段
            match self.match_single_segment(
                track_segment,
                &available_segments,
                &classification_records,
                &project_materials,
                &mut local_used_segment_ids,
            ).await {
                Ok(segment_match) => {
                    // 收集使用的模特ID
                    if let Some(model_name) = &segment_match.model_name {
                        if !model_name.is_empty() {
                            used_model_ids.insert(model_name.clone());
                        }
                    }
                    matches.push(segment_match);
                }
                Err(error_msg) => {
                    failed_segments.push(FailedSegmentMatch {
                        track_segment_id: track_segment.id.clone(),
                        track_segment_name: track_segment.name.clone(),
                        matching_rule: track_segment.matching_rule.clone(),
                        failure_reason: error_msg,
                    });
                }
            }
        }

        // 计算统计信息
        let total_segments = track_segments.len() as u32;
        let matched_segments = matches.len() as u32;
        let failed_segments_count = failed_segments.len() as u32;
        let fixed_segments_count = fixed_segments.len() as u32;

        // 正确的成功率计算：(成功匹配的片段 + 固定片段) / 总片段数
        let successful_segments = matched_segments + fixed_segments_count;
        let success_rate = if total_segments > 0 {
            successful_segments as f64 / total_segments as f64
        } else {
            1.0 // 如果没有片段，成功率为100%
        };

        // 判断匹配是否成功：所有需要匹配的片段都成功匹配
        let is_matching_successful = failed_segments_count == 0;

        // 创建匹配结果
        let matching_result = MaterialMatchingResult {
            binding_id: request.binding_id.clone(),
            template_id: request.template_id.clone(),
            project_id: request.project_id.clone(),
            matches,
            statistics: MatchingStatistics {
                total_segments, // 统计所有片段
                matched_segments,
                failed_segments: failed_segments_count,
                success_rate,
                used_materials: local_used_segment_ids.len() as u32,
                used_models: used_model_ids.len() as u32,
            },
            failed_segments,
        };

        // 只有匹配完全成功时才保存到数据库和记录资源使用
        let (saved_result, final_used_segments) = if is_matching_successful {
            // 保存匹配结果到数据库
            let saved_result = if let Some(result_service) = &self.matching_result_service {
                let saved = result_service.save_matching_result(
                    &matching_result,
                    result_name,
                    None,
                    0, // 匹配耗时，这里简化为0
                ).await?;

                // 创建素材使用记录
                let usage_requests: Vec<crate::data::models::material_usage::CreateMaterialUsageRecordRequest> = matching_result.matches
                    .iter()
                    .map(|segment_match| {
                        let usage_context = serde_json::json!({
                            "match_score": segment_match.match_score,
                            "match_reason": segment_match.match_reason,
                            "material_name": segment_match.material_name,
                            "model_name": segment_match.model_name
                        }).to_string();

                        crate::data::models::material_usage::CreateMaterialUsageRecordRequest {
                            material_segment_id: segment_match.material_segment_id.clone(),
                            material_id: segment_match.material_segment.material_id.clone(),
                            project_id: request.project_id.clone(),
                            template_matching_result_id: saved.id.clone(),
                            template_id: request.template_id.clone(),
                            binding_id: request.binding_id.clone(),
                            track_segment_id: segment_match.track_segment_id.clone(),
                            usage_type: crate::data::models::material_usage::MaterialUsageType::TemplateMatching,
                            usage_context: Some(usage_context),
                        }
                    })
                    .collect();

                if !usage_requests.is_empty() {
                    match self.material_usage_repo.create_usage_records_batch(usage_requests) {
                        Ok(_usage_records) => {
                            // 素材使用记录创建成功
                        }
                        Err(_e) => {
                            // 创建素材使用记录失败，但不阻断主流程
                        }
                    }
                }

                Some(saved)
            } else {
                None
            };

            (saved_result, local_used_segment_ids)
        } else {
            // 匹配失败时不保存到数据库，也不记录资源使用
            (None, HashSet::new())
        };

        Ok((matching_result, saved_result, final_used_segments))
    }

    /// 计算批量匹配汇总信息
    fn calculate_batch_summary(&self, results: &[BatchMatchingItemResult]) -> BatchMatchingSummary {
        let mut total_segments_matched = 0u32;
        let mut total_materials_used = 0u32;
        let mut total_models_used = 0u32;
        let mut success_rates = Vec::new();
        let mut best_template: Option<(String, f64)> = None;
        let mut worst_template: Option<(String, f64)> = None;

        for result in results {
            if let Some(matching_result) = &result.matching_result {
                total_segments_matched += matching_result.statistics.matched_segments;
                total_materials_used += matching_result.statistics.used_materials;
                total_models_used += matching_result.statistics.used_models;

                let success_rate = matching_result.statistics.success_rate;
                success_rates.push(success_rate);

                // 更新最佳和最差模板
                match &best_template {
                    None => best_template = Some((result.template_name.clone(), success_rate)),
                    Some((_, best_rate)) if success_rate > *best_rate => {
                        best_template = Some((result.template_name.clone(), success_rate));
                    }
                    _ => {}
                }

                match &worst_template {
                    None => worst_template = Some((result.template_name.clone(), success_rate)),
                    Some((_, worst_rate)) if success_rate < *worst_rate => {
                        worst_template = Some((result.template_name.clone(), success_rate));
                    }
                    _ => {}
                }
            }
        }

        let average_success_rate = if success_rates.is_empty() {
            0.0
        } else {
            success_rates.iter().sum::<f64>() / success_rates.len() as f64
        };

        BatchMatchingSummary {
            total_segments_matched,
            total_materials_used,
            total_models_used,
            average_success_rate,
            best_matching_template: best_template.map(|(name, _)| name),
            worst_matching_template: worst_template.map(|(name, _)| name),
        }
    }

    /// 按模板片段权重顺序匹配素材
    async fn match_by_template_segment_priority_order(
        &self,
        track_segment: &TrackSegment,
        available_segments: &[(MaterialSegment, String)],
        category_ids: &[String],
        project_materials: &[Material],
        used_segment_ids: &mut HashSet<String>,
        template_already_used_sequence_001: bool,
        weight_service: &Arc<TemplateSegmentWeightService>,
    ) -> Result<SegmentMatch, String> {
        let _target_duration = track_segment.duration as f64 / 1_000_000.0; // 转换为秒

        // 从track_segment获取template_id
        // 注意：这里需要从track_segment的track_id获取template_id
        // 我们需要通过template_service来获取这个信息
        let template_id = match self.get_template_id_from_track_segment(track_segment).await {
            Ok(id) => id,
            Err(_) => {
                // 如果无法获取template_id，回退到全局权重匹配
                return self.match_by_priority_order(
                    track_segment,
                    available_segments,
                    category_ids,
                    project_materials,
                    used_segment_ids,
                    template_already_used_sequence_001,
                ).await;
            }
        };

        // 获取模板片段的AI分类按权重排序
        let ai_classifications = match weight_service.get_classifications_by_segment_weight(&template_id, &track_segment.id).await {
            Ok(classifications) => classifications,
            Err(_) => {
                // 如果获取失败，回退到全局权重匹配
                return self.match_by_priority_order(
                    track_segment,
                    available_segments,
                    category_ids,
                    project_materials,
                    used_segment_ids,
                    template_already_used_sequence_001,
                ).await;
            }
        };

        // 按权重顺序尝试匹配每个分类
        for classification in ai_classifications {
            // 检查当前分类是否在指定的分类列表中
            if !category_ids.contains(&classification.id) {
                continue;
            }

            // 尝试匹配当前分类的素材
            let matching_result = self.match_by_ai_classification(
                track_segment,
                available_segments,
                &classification.name,
                project_materials,
                used_segment_ids,
                template_already_used_sequence_001,
            ).await;

            // 如果匹配成功，返回结果
            if let Ok(segment_match) = matching_result {
                return Ok(SegmentMatch {
                    track_segment_id: segment_match.track_segment_id,
                    track_segment_name: segment_match.track_segment_name,
                    material_segment_id: segment_match.material_segment_id,
                    material_segment: segment_match.material_segment,
                    material_name: segment_match.material_name,
                    model_name: segment_match.model_name,
                    match_score: segment_match.match_score,
                    match_reason: format!("按模板片段权重匹配: {} (权重: {})", classification.name, classification.weight),
                });
            }
        }

        Err("按模板片段权重顺序匹配失败：没有找到合适的素材".to_string())
    }

    /// 从track_segment获取template_id的辅助方法
    async fn get_template_id_from_track_segment(&self, _track_segment: &TrackSegment) -> Result<String, String> {
        // 临时实现：由于get_track_by_id方法可能不存在，先返回错误
        // TODO: 实现正确的template_id获取逻辑
        Err("暂未实现template_id获取功能".to_string())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashSet;

    #[test]
    fn test_loop_termination_when_no_materials_available() {
        // Test case: Loop should terminate when no materials are available for any template
        let round_successful_matches = 0;
        let should_terminate = round_successful_matches == 0;

        assert!(should_terminate, "Loop should terminate when no successful matches in a round");
    }

    #[test]
    fn test_loop_continues_when_materials_available() {
        // Test case: Loop should continue when there are successful matches
        let round_successful_matches = 2;
        let should_terminate = round_successful_matches == 0;

        assert!(!should_terminate, "Loop should continue when there are successful matches");
    }

    #[test]
    fn test_max_rounds_limit() {
        // Test case: Loop should terminate when max rounds limit is reached
        let total_rounds = 100;
        let max_rounds = 100;
        let should_terminate = total_rounds >= max_rounds;

        assert!(should_terminate, "Loop should terminate when max rounds limit is reached");
    }

    #[test]
    fn test_global_used_segments_tracking() {
        // Test case: Global used segments should be tracked correctly across rounds
        let mut global_used_segments = HashSet::new();

        // Simulate first round usage
        global_used_segments.insert("seg1".to_string());
        global_used_segments.insert("seg2".to_string());

        assert_eq!(global_used_segments.len(), 2, "Should track 2 used segments after first round");

        // Simulate second round usage
        global_used_segments.insert("seg3".to_string());
        global_used_segments.insert("seg1".to_string()); // Duplicate, should not increase count

        assert_eq!(global_used_segments.len(), 3, "Should track 3 unique used segments after second round");

        // Check if segment is already used
        assert!(global_used_segments.contains("seg1"), "Should contain seg1 as used");
        assert!(!global_used_segments.contains("seg4"), "Should not contain seg4 as used");
    }

    #[test]
    fn test_batch_matching_result_structure() {
        // Test case: BatchMatchingResult should contain all required loop information
        let result = BatchMatchingResult {
            project_id: "test_project".to_string(),
            total_bindings: 5,
            successful_matches: 3,
            failed_matches: 2,
            skipped_bindings: 0,
            matching_results: Vec::new(),
            total_duration_ms: 5000,
            summary: BatchMatchingSummary {
                total_segments_matched: 10,
                total_materials_used: 8,
                total_models_used: 2,
                average_success_rate: 0.75,
                best_matching_template: Some("Template A".to_string()),
                worst_matching_template: Some("Template B".to_string()),
            },
            total_rounds: 3,
            successful_rounds: 2,
            termination_reason: "第3轮无任何成功匹配，素材已耗尽".to_string(),
            materials_exhausted: true,
        };

        assert_eq!(result.total_rounds, 3, "Should track total rounds correctly");
        assert_eq!(result.successful_rounds, 2, "Should track successful rounds correctly");
        assert!(result.materials_exhausted, "Should indicate materials exhausted");
        assert!(result.termination_reason.contains("素材已耗尽"), "Should contain termination reason");
    }

    #[test]
    fn test_performance_optimization_early_termination() {
        // Test case: Performance optimization should trigger early termination
        let total_rounds = 6; // Should trigger check on round 6 (6 % 5 == 1)
        let should_check = total_rounds % 5 == 1 && total_rounds > 1;

        assert!(should_check, "Should trigger performance check on round 6");

        let total_rounds = 3; // Should not trigger check
        let should_check = total_rounds % 5 == 1 && total_rounds > 1;

        assert!(!should_check, "Should not trigger performance check on round 3");
    }

    #[test]
    fn test_logging_optimization() {
        // Test case: Logging should be optimized for performance
        let total_rounds = 3;
        let should_log = total_rounds <= 5 || total_rounds % 10 == 0;

        assert!(should_log, "Should log for early rounds (≤5)");

        let total_rounds = 20;
        let should_log = total_rounds <= 5 || total_rounds % 10 == 0;

        assert!(should_log, "Should log for round 20 (multiple of 10)");

        let total_rounds = 7;
        let should_log = total_rounds <= 5 || total_rounds % 10 == 0;

        assert!(!should_log, "Should not log for round 7");
    }
}
