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

use super::executor::PythonExecutor;

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

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CutResult {
    pub frame_count: u64,
    pub fps: f64,
    pub scenes: Vec<CutScene>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CutScene {
    pub scene_number: u32,
    pub start_frame: u64,
    pub end_frame: u64,
    pub start_time: f64,
    pub end_time: f64,
}

pub async fn process_cut(
    video_path: &str,
    output_path: &str,
    uuid: Option<&str>,
) -> Result<CutResult> {
    let executor = PythonExecutor::new()?;
    let script_path = executor.script_path("cut_processor.py");

    tracing::info!("[CUT] Starting scene detection: {}", video_path);

    if !script_path.exists() {
        tracing::warn!("[CUT] Script not found, returning empty result");
        return Ok(CutResult {
            frame_count: 0,
            fps: 0.0,
            scenes: vec![],
        });
    }

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

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

    let result: CutResult =
        serde_json::from_str(&json_str).context("Failed to parse CUT output")?;

    tracing::info!("[CUT] Result: {} scenes detected", result.scenes.len());

    Ok(result)
}

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

    #[test]
    fn test_cut_result_serialization() {
        let result = CutResult {
            frame_count: 100,
            fps: 30.0,
            scenes: vec![CutScene {
                scene_number: 1,
                start_frame: 0,
                end_frame: 30,
                start_time: 0.0,
                end_time: 1.0,
            }],
        };

        let json = serde_json::to_string(&result).unwrap();
        assert!(json.contains("scene_number"));
        assert!(json.contains("1"));
    }

    #[test]
    fn test_cut_result_deserialization() {
        let json = r#"{
            "frame_count": 100,
            "fps": 30.0,
            "scenes": [
                {"scene_number": 1, "start_frame": 0, "end_frame": 30, "start_time": 0.0, "end_time": 1.0},
                {"scene_number": 2, "start_frame": 31, "end_frame": 60, "start_time": 1.033, "end_time": 2.0}
            ]
        }"#;

        let result: CutResult = serde_json::from_str(json).unwrap();
        assert_eq!(result.frame_count, 100);
        assert_eq!(result.scenes.len(), 2);
        assert_eq!(result.scenes[1].scene_number, 2);
    }

    #[test]
    fn test_cut_result_empty_scenes() {
        let result = CutResult {
            frame_count: 0,
            fps: 0.0,
            scenes: vec![],
        };
        assert!(result.scenes.is_empty());
    }

    #[test]
    fn test_cut_scene_times() {
        let scene = CutScene {
            scene_number: 1,
            start_frame: 0,
            end_frame: 30,
            start_time: 0.0,
            end_time: 1.0,
        };
        assert!(scene.end_time > scene.start_time);
        assert_eq!(scene.scene_number, 1);
    }
}
