use crate::core::time::FrameTime;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ChunkType {
    TimeBased,
    Sentence,
    Cut,
    Trace,
    Story,  // Parent chunk from story analysis
    Visual, // Visual object-based chunk from YOLO detection (Phase 2.1)
}

impl ChunkType {
    pub fn as_str(&self) -> &'static str {
        match self {
            ChunkType::TimeBased => "time",
            ChunkType::Sentence => "sentence",
            ChunkType::Cut => "cut",
            ChunkType::Trace => "trace",
            ChunkType::Story => "story",
            ChunkType::Visual => "visual",
        }
    }
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ChunkRule {
    Rule1, // 直接轉換
    Rule2, // 集合內容
}

/// 關鍵幀的物件列表
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyframeObjects {
    /// 關鍵幀時間 (秒)
    pub timestamp: f64,
    /// 關鍵幀幀號
    pub frame_number: u64,
    /// 檢測到的物件
    pub objects: Vec<DetectedObject>,
}

/// 檢測到的物件
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectedObject {
    /// 物件類別名稱
    pub class_name: String,
    /// 物件類別 ID
    pub class_id: u32,
    /// 信心值 (0.0-1.0)
    pub confidence: f32,
    /// 邊界框 (x, y, width, height)
    pub bbox: Option<BoundingBox>,
    /// 出現次數 (在分片內)
    pub occurrence: u32,
}

/// 邊界框
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoundingBox {
    pub x: i32,
    pub y: i32,
    pub width: i32,
    pub height: i32,
}

/// 視覺分片內容 (Phase 2.1)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VisualChunkContent {
    pub start_time: f64,
    pub end_time: f64,
    pub keyframe_objects: Vec<KeyframeObjects>,
    pub dominant_objects: Vec<String>,
    pub object_relationships: Vec<(String, String, String)>, // (object1, relationship, object2)
    pub scene_description: Option<String>,
    pub metadata: VisualMetadata,
}

/// 視覺元數據 (Phase 2.1)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VisualMetadata {
    pub object_count: u32,
    pub unique_classes: Vec<String>,
    pub max_confidence: f32,
    pub avg_confidence: f32,
    pub spatial_density: f32, // objects per frame
}

impl ChunkRule {
    pub fn as_str(&self) -> &'static str {
        match self {
            ChunkRule::Rule1 => "rule_1",
            ChunkRule::Rule2 => "rule_2",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Chunk {
    pub file_id: i32,
    pub uuid: String,
    pub chunk_id: String,
    pub chunk_index: u32,
    pub chunk_type: ChunkType,
    pub rule: ChunkRule,
    /// Frames per second (can be fractional, e.g., 29.97, 23.976)
    pub fps: f64,
    /// Start frame (0-based)
    pub start_frame: i64,
    /// End frame (exclusive)
    pub end_frame: i64,
    pub text_content: Option<String>,
    pub content: serde_json::Value,
    pub metadata: Option<serde_json::Value>,
    pub vector_id: Option<String>,
    pub frame_count: i32,
    pub pre_chunk_ids: Vec<i32>,
    pub parent_chunk_id: Option<String>, // For parent-child chunk hierarchy
    pub child_chunk_ids: Vec<String>,    // Child chunk IDs (for parent chunks)
    pub visual_stats: Option<serde_json::Value>,
}

impl Chunk {
    /// 創建視覺分片 (Phase 2.1)
    pub fn new_visual(
        file_id: i32,
        uuid: String,
        chunk_index: u32,
        start_frame: i64,
        end_frame: i64,
        fps: f64,
        visual_content: VisualChunkContent,
    ) -> Self {
        let content = serde_json::to_value(&visual_content)
            .unwrap_or_else(|_| serde_json::json!({"error": "Failed to serialize visual content"}));

        Self::new(
            file_id,
            uuid,
            chunk_index,
            ChunkType::Visual,
            ChunkRule::Rule2,
            start_frame,
            end_frame,
            fps,
            content,
        )
    }

    /// 從 YOLO 結果創建視覺分片 (Phase 2.1)
    pub fn from_yolo_result(
        file_id: i32,
        uuid: String,
        chunk_index: u32,
        start_frame: i64,
        end_frame: i64,
        fps: f64,
        yolo_frames: Vec<crate::core::processor::yolo::YoloFrame>,
    ) -> Self {
        let keyframe_objects: Vec<KeyframeObjects> = yolo_frames
            .iter()
            .map(|frame| {
                let objects: Vec<DetectedObject> = frame
                    .objects
                    .iter()
                    .map(|obj| DetectedObject {
                        class_name: obj.class_name.clone(),
                        class_id: obj.class_id,
                        confidence: obj.confidence,
                        bbox: Some(BoundingBox {
                            x: obj.x,
                            y: obj.y,
                            width: obj.width,
                            height: obj.height,
                        }),
                        occurrence: 1,
                    })
                    .collect();

                KeyframeObjects {
                    timestamp: frame.timestamp,
                    frame_number: frame.frame,
                    objects,
                }
            })
            .collect();

        // 計算物件統計
        let mut object_counts = std::collections::HashMap::new();
        for obj in yolo_frames.iter().flat_map(|f| &f.objects) {
            *object_counts.entry(obj.class_name.clone()).or_insert(0) += 1;
        }

        let total_objects: u32 = yolo_frames.iter().map(|f| f.objects.len() as u32).sum();
        let all_classes: Vec<String> = yolo_frames
            .iter()
            .flat_map(|f| f.objects.iter().map(|o| o.class_name.clone()))
            .collect();
        let unique_classes: Vec<String> = all_classes
            .iter()
            .cloned()
            .collect::<std::collections::HashSet<_>>()
            .into_iter()
            .collect();

        let confidences: Vec<f32> = yolo_frames
            .iter()
            .flat_map(|f| f.objects.iter().map(|o| o.confidence))
            .collect();
        let max_confidence = confidences.iter().copied().fold(0.0f32, f32::max);
        let avg_confidence = if !confidences.is_empty() {
            confidences.iter().sum::<f32>() / confidences.len() as f32
        } else {
            0.0
        };

        // 找出主要物件
        let primary_objects = object_counts
            .iter()
            .filter(|(_, &count)| count as f32 / yolo_frames.len() as f32 > 0.5)
            .map(|(name, _)| name.clone())
            .collect::<Vec<_>>()
            .join(", ");

        let object_stats =
            serde_json::to_value(&object_counts).unwrap_or_else(|_| serde_json::json!({}));

        let visual_content = VisualChunkContent {
            start_time: if let Some(first) = yolo_frames.first() {
                first.timestamp
            } else {
                0.0
            },
            end_time: if let Some(last) = yolo_frames.last() {
                last.timestamp
            } else {
                0.0
            },
            keyframe_objects,
            dominant_objects: primary_objects
                .split(", ")
                .map(|s| s.to_string())
                .filter(|s| !s.is_empty())
                .collect(),
            object_relationships: vec![], // 可選：後續添加關係檢測
            scene_description: None,      // 可選：後續添加 LLM 生成的場景描述
            metadata: VisualMetadata {
                object_count: total_objects,
                unique_classes,
                max_confidence,
                avg_confidence,
                spatial_density: if yolo_frames.len() > 0 {
                    total_objects as f32 / yolo_frames.len() as f32
                } else {
                    0.0
                },
            },
        };

        Self::new_visual(
            file_id,
            uuid,
            chunk_index,
            start_frame,
            end_frame,
            fps,
            visual_content,
        )
    }

    /// 創建新分片
    pub fn new(
        file_id: i32,
        uuid: String,
        chunk_index: u32,
        chunk_type: ChunkType,
        rule: ChunkRule,
        start_frame: i64,
        end_frame: i64,
        fps: f64,
        content: serde_json::Value,
    ) -> Self {
        let frame_count = (end_frame - start_frame) as i32;
        let chunk_id = format!("{}_{}", uuid, chunk_index);

        Self {
            file_id,
            uuid,
            chunk_id,
            chunk_index,
            chunk_type,
            rule,
            fps,
            start_frame,
            end_frame,
            text_content: None,
            content,
            metadata: None,
            vector_id: None,
            frame_count,
            pre_chunk_ids: vec![],
            parent_chunk_id: None,
            child_chunk_ids: vec![],
            visual_stats: None,
        }
    }

    /// 將分片轉換為幀時間
    pub fn to_frame_time(&self) -> FrameTime {
        FrameTime::from_frames(self.start_frame as u64, self.end_frame as u64, self.fps)
    }

    /// 檢查是否是父分片
    pub fn is_parent(&self) -> bool {
        self.parent_chunk_id.is_some()
    }
}
