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

use super::executor::PythonExecutor;

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

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct StoryResult {
    pub child_chunks: Vec<StoryChildChunk>,
    pub parent_chunks: Vec<StoryParentChunk>,
    pub stats: StoryStats,
    pub metadata: serde_json::Value,
    pub parent_chunk_size: usize,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct StoryStats {
    pub total_child_chunks: usize,
    pub total_parent_chunks: usize,
    pub asr_children: usize,
    pub cut_children: usize,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct StoryChildChunk {
    pub chunk_id: String,
    pub chunk_type: String,
    pub source: String,
    pub start_time: f64,
    pub end_time: f64,
    pub text_content: Option<String>,
    pub content: serde_json::Value,
    pub child_chunk_ids: Vec<String>,
    pub parent_chunk_id: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct StoryParentChunk {
    pub chunk_id: String,
    pub chunk_type: String,
    pub source: String,
    pub start_time: f64,
    pub end_time: f64,
    pub text_content: String,
    pub content: serde_json::Value,
    pub child_chunk_ids: Vec<String>,
    pub parent_chunk_id: Option<String>,
}

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

    tracing::info!("[STORY] Starting story generation: {}", video_path);

    if !script_path.exists() {
        tracing::warn!("[STORY] Script not found, returning empty result");
        return Ok(StoryResult {
            child_chunks: vec![],
            parent_chunks: vec![],
            stats: StoryStats {
                total_child_chunks: 0,
                total_parent_chunks: 0,
                asr_children: 0,
                cut_children: 0,
            },
            metadata: serde_json::json!({}),
            parent_chunk_size: 5,
        });
    }

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

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

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

    tracing::info!(
        "[STORY] Result: {} parent chunks, {} child chunks",
        result.stats.total_parent_chunks,
        result.stats.total_child_chunks
    );

    Ok(result)
}

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

    #[test]
    fn test_story_result_serialization() {
        let result = StoryResult {
            child_chunks: vec![StoryChildChunk {
                chunk_id: "asr_0001".to_string(),
                chunk_type: "sentence".to_string(),
                source: "asr".to_string(),
                start_time: 0.0,
                end_time: 5.0,
                text_content: Some("Hello world".to_string()),
                content: serde_json::json!({}),
                child_chunk_ids: vec![],
                parent_chunk_id: Some("story_asr_0000".to_string()),
            }],
            parent_chunks: vec![StoryParentChunk {
                chunk_id: "story_asr_0000".to_string(),
                chunk_type: "story".to_string(),
                source: "story_asr".to_string(),
                start_time: 0.0,
                end_time: 25.0,
                text_content: "[0s-25s] Hello world...".to_string(),
                content: serde_json::json!({
                    "description": "[0s-25s] Hello world...",
                    "child_count": 5
                }),
                child_chunk_ids: vec!["asr_0001".to_string()],
                parent_chunk_id: None,
            }],
            stats: StoryStats {
                total_child_chunks: 10,
                total_parent_chunks: 2,
                asr_children: 10,
                cut_children: 0,
            },
            metadata: serde_json::json!({}),
            parent_chunk_size: 5,
        };

        let json = serde_json::to_string(&result).unwrap();
        assert!(json.contains("asr_0001"));
        assert!(json.contains("story_asr_0000"));
        assert!(json.contains("Hello world"));
    }

    #[test]
    fn test_story_result_deserialization() {
        let json = r#"{
            "child_chunks": [{
                "chunk_id": "asr_0001",
                "chunk_type": "sentence",
                "source": "asr",
                "start_time": 0.0,
                "end_time": 5.0,
                "text_content": "Hello",
                "content": {},
                "child_chunk_ids": [],
                "parent_chunk_id": null
            }],
            "parent_chunks": [{
                "chunk_id": "story_asr_0000",
                "chunk_type": "story",
                "source": "story_asr",
                "start_time": 0.0,
                "end_time": 5.0,
                "text_content": "Hello segment",
                "content": {"description": "Hello segment"},
                "child_chunk_ids": ["asr_0001"],
                "parent_chunk_id": null
            }],
            "stats": {
                "total_child_chunks": 1,
                "total_parent_chunks": 1,
                "asr_children": 1,
                "cut_children": 0
            },
            "metadata": {},
            "parent_chunk_size": 5
        }"#;

        let result: StoryResult = serde_json::from_str(json).unwrap();
        assert_eq!(result.child_chunks.len(), 1);
        assert_eq!(result.parent_chunks.len(), 1);
        assert_eq!(result.stats.total_child_chunks, 1);
        assert_eq!(result.stats.total_parent_chunks, 1);
        assert_eq!(result.parent_chunks[0].child_chunk_ids[0], "asr_0001");
        assert_eq!(result.child_chunks[0].parent_chunk_id, None);
    }

    #[test]
    fn test_parent_child_relationship() {
        let result = StoryResult {
            child_chunks: vec![
                StoryChildChunk {
                    chunk_id: "asr_0001".to_string(),
                    chunk_type: "sentence".to_string(),
                    source: "asr".to_string(),
                    start_time: 0.0,
                    end_time: 5.0,
                    text_content: Some("First".to_string()),
                    content: serde_json::json!({}),
                    child_chunk_ids: vec![],
                    parent_chunk_id: Some("story_asr_0000".to_string()),
                },
                StoryChildChunk {
                    chunk_id: "asr_0002".to_string(),
                    chunk_type: "sentence".to_string(),
                    source: "asr".to_string(),
                    start_time: 5.0,
                    end_time: 10.0,
                    text_content: Some("Second".to_string()),
                    content: serde_json::json!({}),
                    child_chunk_ids: vec![],
                    parent_chunk_id: Some("story_asr_0000".to_string()),
                },
            ],
            parent_chunks: vec![StoryParentChunk {
                chunk_id: "story_asr_0000".to_string(),
                chunk_type: "story".to_string(),
                source: "story_asr".to_string(),
                start_time: 0.0,
                end_time: 10.0,
                text_content: "Combined narrative".to_string(),
                content: serde_json::json!({}),
                child_chunk_ids: vec!["asr_0001".to_string(), "asr_0002".to_string()],
                parent_chunk_id: None,
            }],
            stats: StoryStats {
                total_child_chunks: 2,
                total_parent_chunks: 1,
                asr_children: 2,
                cut_children: 0,
            },
            metadata: serde_json::json!({}),
            parent_chunk_size: 5,
        };

        assert_eq!(result.parent_chunks[0].child_chunk_ids.len(), 2);
        assert!(result
            .child_chunks
            .iter()
            .all(|c| c.parent_chunk_id.is_some()));
        assert!(result.parent_chunks[0].parent_chunk_id.is_none());
    }
}
