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

use super::executor::PythonExecutor;

const OCR_TIMEOUT: Duration = Duration::from_secs(7200);

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct OcrResult {
    pub frame_count: u64,
    pub fps: f64,
    pub frames: Vec<OcrFrame>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct OcrFrame {
    pub frame: u64,
    pub timestamp: f64,
    pub texts: Vec<OcrText>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct OcrText {
    pub text: String,
    pub x: i32,
    pub y: i32,
    pub width: i32,
    pub height: i32,
    pub confidence: f32,
}

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

    tracing::info!("[OCR] Starting text recognition: {}", video_path);

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

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

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

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

    tracing::info!("[OCR] Result: {} frames", result.frames.len());

    Ok(result)
}

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

    #[test]
    fn test_ocr_result_serialization() {
        let result = OcrResult {
            frame_count: 100,
            fps: 30.0,
            frames: vec![OcrFrame {
                frame: 0,
                timestamp: 0.0,
                texts: vec![OcrText {
                    text: "Hello".to_string(),
                    x: 10,
                    y: 20,
                    width: 100,
                    height: 30,
                    confidence: 0.95,
                }],
            }],
        };

        let json = serde_json::to_string(&result).unwrap();
        assert!(json.contains("Hello"));
        assert!(json.contains("\"x\":10"));
    }

    #[test]
    fn test_ocr_result_deserialization() {
        let json = r#"{
            "frame_count": 50,
            "fps": 25.0,
            "frames": [
                {
                    "frame": 30,
                    "timestamp": 1.2,
                    "texts": [
                        {"text": "Test", "x": 0, "y": 0, "width": 50, "height": 20, "confidence": 0.88}
                    ]
                }
            ]
        }"#;

        let result: OcrResult = serde_json::from_str(json).unwrap();
        assert_eq!(result.frame_count, 50);
        assert_eq!(result.frames.len(), 1);
        assert_eq!(result.frames[0].texts[0].text, "Test");
    }

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

    #[test]
    fn test_ocr_text_confidence() {
        let text = OcrText {
            text: "OCR".to_string(),
            x: 0,
            y: 0,
            width: 10,
            height: 10,
            confidence: 0.75,
        };
        assert!(text.confidence >= 0.0 && text.confidence <= 1.0);
    }
}
