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

use super::executor::PythonExecutor;

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

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

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FaceFrame {
    pub frame: u64,
    pub timestamp: f64,
    pub faces: Vec<Face>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Face {
    pub face_id: Option<String>,
    pub x: i32,
    pub y: i32,
    pub width: i32,
    pub height: i32,
    pub confidence: f32,
    pub embedding: Option<Vec<f32>>,
    pub landmarks: Option<Vec<Vec<f32>>>,
    pub attributes: Option<FaceAttributes>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FaceAttributes {
    pub age: Option<i32>,
    pub gender: Option<String>,
}

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

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

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

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

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

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

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

    Ok(result)
}

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

    #[test]
    fn test_face_result_serialization() {
        let result = FaceResult {
            frame_count: 100,
            fps: 30.0,
            frames: vec![FaceFrame {
                frame: 0,
                timestamp: 0.0,
                faces: vec![Face {
                    face_id: Some("face_1".to_string()),
                    x: 100,
                    y: 100,
                    width: 50,
                    height: 60,
                    confidence: 0.95,
                    embedding: Some(vec![0.1, 0.2, 0.3]),
                    landmarks: Some(vec![vec![10.0, 20.0], vec![30.0, 40.0]]),
                    attributes: Some(FaceAttributes {
                        age: Some(30),
                        gender: Some("male".to_string()),
                    }),
                }],
            }],
        };

        let json = serde_json::to_string(&result).unwrap();
        assert!(json.contains("face_1"));
        assert!(json.contains("\"width\":50"));
        assert!(json.contains("embedding"));
        assert!(json.contains("landmarks"));
        assert!(json.contains("attributes"));
    }

    #[test]
    fn test_face_result_deserialization() {
        let json = r#"{
            "frame_count": 50,
            "fps": 25.0,
            "frames": [
                {
                    "frame": 30,
                    "timestamp": 1.2,
                    "faces": [
                        {
                            "face_id": "f1",
                            "x": 10,
                            "y": 20,
                            "width": 30,
                            "height": 40,
                            "confidence": 0.85,
                            "embedding": [0.1, 0.2, 0.3],
                            "landmarks": [[5.0, 10.0]],
                            "attributes": {"age": 25, "gender": "female"}
                        }
                    ]
                }
            ]
        }"#;

        let result: FaceResult = serde_json::from_str(json).unwrap();
        assert_eq!(result.frame_count, 50);
        assert_eq!(result.frames.len(), 1);
        assert_eq!(result.frames[0].faces[0].x, 10);
        assert!(result.frames[0].faces[0].embedding.is_some());
        assert!(result.frames[0].faces[0].landmarks.is_some());
        assert!(result.frames[0].faces[0].attributes.is_some());
    }

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

    #[test]
    fn test_face_confidence() {
        let face = Face {
            face_id: None,
            x: 0,
            y: 0,
            width: 10,
            height: 10,
            confidence: 0.5,
            embedding: None,
            landmarks: None,
            attributes: None,
        };
        assert!(face.confidence >= 0.0 && face.confidence <= 1.0);
    }

    #[test]
    fn test_face_with_embedding() {
        let face = Face {
            face_id: Some("face_001".to_string()),
            x: 100,
            y: 200,
            width: 50,
            height: 60,
            confidence: 0.95,
            embedding: Some(vec![0.1; 512]),
            landmarks: None,
            attributes: Some(FaceAttributes {
                age: Some(35),
                gender: Some("male".to_string()),
            }),
        };

        assert!(face.embedding.is_some());
        let embedding = face.embedding.unwrap();
        assert_eq!(embedding.len(), 512);
        assert_eq!(embedding[0], 0.1);
    }
}
