//! 視覺分片處理器 (Phase 2.2)
//!
//! 從 YOLO 結果生成視覺分片

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::time::Duration;

use super::executor::PythonExecutor;
use super::yolo::{YoloFrame, YoloResult};

const VISUAL_CHUNK_TIMEOUT: Duration = Duration::from_secs(3600);

/// 視覺分片處理結果
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct VisualChunkResult {
    /// 生成的視覺分片數量
    pub chunk_count: u32,
    /// 處理的總幀數
    pub total_frames: u32,
    /// 檢測到的總物件數
    pub total_objects: u32,
    /// 唯一物件類別數
    pub unique_classes: u32,
    /// 生成的視覺分片
    pub chunks: Vec<crate::core::chunk::Chunk>,
}

/// 從 YOLO 結果生成視覺分片
pub async fn process_visual_chunk(
    file_id: i32,
    uuid: String,
    video_path: &str,
    yolo_result: &YoloResult,
    chunk_index_offset: u32,
    fps: f64,
) -> Result<VisualChunkResult> {
    tracing::info!(
        "[VisualChunk] Starting visual chunk generation for video: {}, {} frames",
        video_path,
        yolo_result.frames.len()
    );

    if yolo_result.frames.is_empty() {
        tracing::warn!("[VisualChunk] No YOLO frames to process");
        return Ok(VisualChunkResult {
            chunk_count: 0,
            total_frames: 0,
            total_objects: 0,
            unique_classes: 0,
            chunks: vec![],
        });
    }

    // 策略 1: 固定幀數分片（每 N 幀一個分片）
    let chunks = create_fixed_frame_chunks(file_id, &uuid, yolo_result, chunk_index_offset, fps);

    // 統計信息
    let total_objects: u32 = yolo_result
        .frames
        .iter()
        .map(|f| f.objects.len() as u32)
        .sum();
    let all_classes: Vec<String> = yolo_result
        .frames
        .iter()
        .flat_map(|f| f.objects.iter().map(|o| o.class_name.clone()))
        .collect();
    let unique_classes: u32 = all_classes
        .iter()
        .cloned()
        .collect::<std::collections::HashSet<_>>()
        .len() as u32;

    tracing::info!(
        "[VisualChunk] Generated {} visual chunks from {} frames, {} total objects, {} unique classes",
        chunks.len(),
        yolo_result.frames.len(),
        total_objects,
        unique_classes
    );

    Ok(VisualChunkResult {
        chunk_count: chunks.len() as u32,
        total_frames: yolo_result.frames.len() as u32,
        total_objects,
        unique_classes,
        chunks,
    })
}

/// 創建固定幀數分片（每 N 幀一個分片）
fn create_fixed_frame_chunks(
    file_id: i32,
    uuid: &str,
    yolo_result: &YoloResult,
    chunk_index_offset: u32,
    fps: f64,
) -> Vec<crate::core::chunk::Chunk> {
    let mut chunks = Vec::new();

    // 配置：每 30 幀創建一個分片（約 1 秒，如果 fps=30）
    let frames_per_chunk = 30;
    let total_frames = yolo_result.frames.len();

    if total_frames == 0 {
        return chunks;
    }

    let mut chunk_index = chunk_index_offset;
    let mut start_idx = 0;

    while start_idx < total_frames {
        let end_idx = std::cmp::min(start_idx + frames_per_chunk, total_frames);

        // 獲取這個分片的幀
        let chunk_frames: Vec<YoloFrame> = yolo_result.frames[start_idx..end_idx]
            .iter()
            .cloned()
            .collect();

        if chunk_frames.is_empty() {
            break;
        }

        // 計算幀範圍
        let start_frame = chunk_frames.first().unwrap().frame as i64;
        let end_frame = chunk_frames.last().unwrap().frame as i64 + 1; // exclusive

        // 創建視覺分片
        let chunk = crate::core::chunk::Chunk::from_yolo_frames(
            file_id,
            uuid.to_string(),
            chunk_index,
            start_frame,
            end_frame,
            fps,
            chunk_frames,
        );

        chunks.push(chunk);

        // 更新索引
        start_idx = end_idx;
        chunk_index += 1;
    }

    chunks
}

/// 基於物件相似度創建分片
fn create_similarity_based_chunks(
    file_id: i32,
    uuid: &str,
    yolo_result: &YoloResult,
    chunk_index_offset: u32,
    fps: f64,
    similarity_threshold: f32,
    min_frames_per_chunk: usize,
) -> Vec<crate::core::chunk::Chunk> {
    let mut chunks = Vec::new();

    if yolo_result.frames.is_empty() {
        return chunks;
    }

    let mut current_chunk_frames: Vec<YoloFrame> = Vec::new();
    let mut chunk_index = chunk_index_offset;
    let mut current_start_frame = 0;

    for (i, frame) in yolo_result.frames.iter().enumerate() {
        if current_chunk_frames.is_empty() {
            current_chunk_frames.push(frame.clone());
            current_start_frame = frame.frame as i64;
            continue;
        }

        // 檢查相似度（簡化版本：檢查物件類別是否相同）
        let last_frame = current_chunk_frames.last().unwrap();
        let similarity = calculate_frame_similarity(last_frame, frame);

        if similarity >= similarity_threshold {
            // 相似度高，加入當前分片
            current_chunk_frames.push(frame.clone());
        } else {
            // 相似度低，創建新分片
            if current_chunk_frames.len() >= min_frames_per_chunk {
                let end_frame = current_chunk_frames.last().unwrap().frame as i64 + 1;

                let chunk = crate::core::chunk::Chunk::from_yolo_frames(
                    file_id,
                    uuid.to_string(),
                    chunk_index,
                    current_start_frame,
                    end_frame,
                    fps,
                    current_chunk_frames.clone(),
                );

                chunks.push(chunk);
                chunk_index += 1;
            }

            // 開始新的分片
            current_chunk_frames = vec![frame.clone()];
            current_start_frame = frame.frame as i64;
        }
    }

    // 處理最後一個分片
    if current_chunk_frames.len() >= min_frames_per_chunk {
        let end_frame = current_chunk_frames.last().unwrap().frame as i64 + 1;

        let chunk = crate::core::chunk::Chunk::from_yolo_frames(
            file_id,
            uuid.to_string(),
            chunk_index,
            current_start_frame,
            end_frame,
            fps,
            current_chunk_frames,
        );

        chunks.push(chunk);
    }

    chunks
}

/// 計算兩個幀之間的相似度（基於物件類別）
fn calculate_frame_similarity(frame1: &YoloFrame, frame2: &YoloFrame) -> f32 {
    if frame1.objects.is_empty() && frame2.objects.is_empty() {
        return 1.0;
    }

    if frame1.objects.is_empty() || frame2.objects.is_empty() {
        return 0.0;
    }

    let set1: std::collections::HashSet<String> = frame1
        .objects
        .iter()
        .map(|o| o.class_name.clone())
        .collect();
    let set2: std::collections::HashSet<String> = frame2
        .objects
        .iter()
        .map(|o| o.class_name.clone())
        .collect();

    let intersection: Vec<_> = set1.intersection(&set2).collect();
    let union: Vec<_> = set1.union(&set2).collect();

    if union.is_empty() {
        0.0
    } else {
        intersection.len() as f32 / union.len() as f32
    }
}

/// 使用 Python 腳本生成視覺分片（進階版本）
pub async fn process_visual_chunk_advanced(
    video_path: &str,
    output_path: &str,
    uuid: Option<&str>,
) -> Result<VisualChunkResult> {
    let executor = PythonExecutor::new()?;
    let script_path = executor.script_path("visual_chunk_processor.py");

    tracing::info!(
        "[VisualChunk] Starting advanced visual chunk generation: {}",
        video_path
    );

    if !script_path.exists() {
        tracing::warn!("[VisualChunk] Script not found, using basic generation");
        // 這裡可以回退到基本生成方法
        return Ok(VisualChunkResult {
            chunk_count: 0,
            total_frames: 0,
            total_objects: 0,
            unique_classes: 0,
            chunks: vec![],
        });
    }

    executor
        .run(
            "visual_chunk_processor.py",
            &[video_path, output_path],
            uuid,
            "VisualChunk",
            Some(VISUAL_CHUNK_TIMEOUT),
        )
        .await
        .with_context(|| format!("Failed to run {:?}", script_path))?;

    let json_str =
        std::fs::read_to_string(output_path).context("Failed to read visual chunk output")?;

    let result: VisualChunkResult =
        serde_json::from_str(&json_str).context("Failed to parse visual chunk output")?;

    tracing::info!(
        "[VisualChunk] Advanced generation result: {} chunks, {} frames",
        result.chunk_count,
        result.total_frames
    );

    Ok(result)
}

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

    #[test]
    fn test_calculate_frame_similarity() {
        use crate::core::processor::yolo::{YoloFrame, YoloObject};

        let frame1 = YoloFrame {
            frame: 0,
            timestamp: 0.0,
            objects: vec![
                YoloObject {
                    class_name: "person".to_string(),
                    class_id: 0,
                    x: 100,
                    y: 200,
                    width: 50,
                    height: 100,
                    confidence: 0.95,
                },
                YoloObject {
                    class_name: "car".to_string(),
                    class_id: 2,
                    x: 300,
                    y: 150,
                    width: 80,
                    height: 60,
                    confidence: 0.87,
                },
            ],
        };

        let frame2 = YoloFrame {
            frame: 1,
            timestamp: 0.033,
            objects: vec![
                YoloObject {
                    class_name: "person".to_string(),
                    class_id: 0,
                    x: 110,
                    y: 210,
                    width: 52,
                    height: 102,
                    confidence: 0.92,
                },
                YoloObject {
                    class_name: "car".to_string(),
                    class_id: 2,
                    x: 310,
                    y: 155,
                    width: 82,
                    height: 62,
                    confidence: 0.85,
                },
            ],
        };

        let frame3 = YoloFrame {
            frame: 2,
            timestamp: 0.066,
            objects: vec![YoloObject {
                class_name: "dog".to_string(),
                class_id: 16,
                x: 150,
                y: 250,
                width: 40,
                height: 60,
                confidence: 0.78,
            }],
        };

        // 相同物件的幀應該高度相似
        let similarity_same = calculate_frame_similarity(&frame1, &frame2);
        assert!((similarity_same - 1.0).abs() < 0.001);

        // 不同物件的幀應該不相似
        let similarity_diff = calculate_frame_similarity(&frame1, &frame3);
        assert!((similarity_diff - 0.0).abs() < 0.001);

        // 空幀應該完全相似
        let empty_frame = YoloFrame {
            frame: 3,
            timestamp: 0.1,
            objects: vec![],
        };
        let similarity_empty = calculate_frame_similarity(&empty_frame, &empty_frame);
        assert!((similarity_empty - 1.0).abs() < 0.001);
    }

    #[tokio::test]
    async fn test_create_fixed_frame_chunks() {
        use crate::core::processor::yolo::{YoloFrame, YoloObject, YoloResult};

        // 創建測試 YOLO 結果（60 幀，每幀都有物件）
        let mut frames = Vec::new();
        for i in 0..60 {
            frames.push(YoloFrame {
                frame: i as u64,
                timestamp: i as f64 / 30.0, // 假設 fps=30
                objects: vec![YoloObject {
                    class_name: "person".to_string(),
                    class_id: 0,
                    x: 100,
                    y: 200,
                    width: 50,
                    height: 100,
                    confidence: 0.9,
                }],
            });
        }

        let yolo_result = YoloResult {
            frame_count: 60,
            fps: 30.0,
            frames,
        };

        let chunks = create_fixed_frame_chunks(1, "test-uuid", &yolo_result, 0, 30.0);

        // 60 幀，每 30 幀一個分片，應該有 2 個分片
        assert_eq!(chunks.len(), 2);

        // 檢查第一個分片
        let first_chunk = &chunks[0];
        assert_eq!(
            first_chunk.chunk_type,
            crate::core::chunk::ChunkType::Visual
        );
        assert_eq!(first_chunk.start_frame, 0);
        assert_eq!(first_chunk.end_frame, 30); // exclusive
        assert_eq!(first_chunk.frame_count, 30);

        // 檢查第二個分片
        let second_chunk = &chunks[1];
        assert_eq!(
            second_chunk.chunk_type,
            crate::core::chunk::ChunkType::Visual
        );
        assert_eq!(second_chunk.start_frame, 30);
        assert_eq!(second_chunk.end_frame, 60); // exclusive
        assert_eq!(second_chunk.frame_count, 30);
    }

    #[test]
    fn test_create_similarity_based_chunks() {
        use crate::core::processor::yolo::{YoloFrame, YoloObject, YoloResult};

        // 創建測試 YOLO 結果
        let frames = vec![
            YoloFrame {
                // 幀 0-4: 都有 person 和 car
                frame: 0,
                timestamp: 0.0,
                objects: vec![
                    YoloObject {
                        class_name: "person".to_string(),
                        class_id: 0,
                        x: 100,
                        y: 200,
                        width: 50,
                        height: 100,
                        confidence: 0.9,
                    },
                    YoloObject {
                        class_name: "car".to_string(),
                        class_id: 2,
                        x: 300,
                        y: 150,
                        width: 80,
                        height: 60,
                        confidence: 0.8,
                    },
                ],
            },
            YoloFrame {
                // 幀 1
                frame: 1,
                timestamp: 0.033,
                objects: vec![
                    YoloObject {
                        class_name: "person".to_string(),
                        class_id: 0,
                        x: 110,
                        y: 210,
                        width: 52,
                        height: 102,
                        confidence: 0.88,
                    },
                    YoloObject {
                        class_name: "car".to_string(),
                        class_id: 2,
                        x: 310,
                        y: 155,
                        width: 82,
                        height: 62,
                        confidence: 0.78,
                    },
                ],
            },
            YoloFrame {
                // 幀 5-9: 只有 dog
                frame: 5,
                timestamp: 0.166,
                objects: vec![YoloObject {
                    class_name: "dog".to_string(),
                    class_id: 16,
                    x: 150,
                    y: 250,
                    width: 40,
                    height: 60,
                    confidence: 0.7,
                }],
            },
            YoloFrame {
                // 幀 6
                frame: 6,
                timestamp: 0.2,
                objects: vec![YoloObject {
                    class_name: "dog".to_string(),
                    class_id: 16,
                    x: 155,
                    y: 255,
                    width: 42,
                    height: 62,
                    confidence: 0.68,
                }],
            },
        ];

        let yolo_result = YoloResult {
            frame_count: 7,
            fps: 30.0,
            frames,
        };

        let chunks = create_similarity_based_chunks(
            1,
            "test-uuid",
            &yolo_result,
            0,
            30.0,
            0.5, // similarity threshold
            2,   // min frames per chunk
        );

        // 應該有 2 個分片：一個是 person+car，一個是 dog
        assert_eq!(chunks.len(), 2);
    }
}
