//! Recording output configuration and results.

use std::path::PathBuf;

use chrono::{DateTime, Duration, Utc};
use serde::{Deserialize, Serialize};

/// Output configuration for recordings.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputConfig {
    /// Output container format.
    pub container: String,
    /// Output file path pattern.
    pub path_pattern: String,
    /// Maximum file size before splitting (MB). 0 = no limit.
    pub max_size_mb: u32,
    /// Maximum duration before splitting (seconds). 0 = no limit.
    pub max_duration_secs: u32,
    /// Whether to use fragmented MP4 for better crash recovery.
    pub fragmented: bool,
}

impl Default for OutputConfig {
    fn default() -> Self {
        Self {
            container: "mp4".to_string(),
            path_pattern: "{date}_{time}".to_string(),
            max_size_mb: 0,
            max_duration_secs: 0,
            fragmented: false,
        }
    }
}

/// Recording output handle.
#[derive(Debug, Clone)]
pub struct RecordingOutput {
    /// Output file path.
    pub path: PathBuf,
    /// Game ID if available.
    pub game_id: Option<u64>,
    /// Champion name if available.
    pub champion: Option<String>,
}

/// Result of a completed recording.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecordingResult {
    /// Output file path.
    pub path: PathBuf,
    /// Game ID if available.
    pub game_id: Option<u64>,
    /// Champion name if available.
    pub champion: Option<String>,
    /// Recording start time.
    pub start_time: DateTime<Utc>,
    /// Recording end time.
    pub end_time: DateTime<Utc>,
    /// Recording duration.
    // #[serde(with = "chrono::serde::seconds")]
    pub duration: Duration,
    /// Recording statistics (if available).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub stats: Option<RecordingStats>,
}

impl RecordingResult {
    /// Get the file size in bytes.
    pub fn file_size(&self) -> Option<u64> {
        std::fs::metadata(&self.path).ok().map(|m| m.len())
    }

    /// Get the file size in a human-readable format.
    pub fn file_size_human(&self) -> String {
        let bytes = self.file_size().unwrap_or(0);
        let mb = bytes as f64 / (1024.0 * 1024.0);
        format!("{:.2} MB", mb)
    }

    /// Get the duration in a human-readable format.
    pub fn duration_human(&self) -> String {
        let total_secs = self.duration.num_seconds();
        let hours = total_secs / 3600;
        let mins = (total_secs % 3600) / 60;
        let secs = total_secs % 60;

        if hours > 0 {
            format!("{}h {}m {}s", hours, mins, secs)
        } else if mins > 0 {
            format!("{}m {}s", mins, secs)
        } else {
            format!("{}s", secs)
        }
    }

    /// Check if the recording file exists.
    pub fn exists(&self) -> bool {
        self.path.exists()
    }

    /// Delete the recording file.
    pub fn delete(&self) -> std::io::Result<()> {
        std::fs::remove_file(&self.path)
    }
}

/// Output format type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OutputFormat {
    /// MP4 container.
    Mp4,
    /// MKV container.
    Mkv,
    /// MOV container.
    Mov,
    /// FLV container.
    Flv,
}

impl OutputFormat {
    /// Get the file extension.
    pub fn extension(&self) -> &'static str {
        match self {
            OutputFormat::Mp4 => "mp4",
            OutputFormat::Mkv => "mkv",
            OutputFormat::Mov => "mov",
            OutputFormat::Flv => "flv",
        }
    }

    /// Get the OBS output ID.
    pub fn obs_output_id(&self) -> &'static str {
        match self {
            OutputFormat::Mp4 => "ffmpeg_muxer",
            OutputFormat::Mkv => "ffmpeg_muxer",
            OutputFormat::Mov => "ffmpeg_muxer",
            OutputFormat::Flv => "ffmpeg_muxer",
        }
    }

    /// Get the FFmpeg format name.
    pub fn ffmpeg_format(&self) -> &'static str {
        match self {
            OutputFormat::Mp4 => "mp4",
            OutputFormat::Mkv => "matroska",
            OutputFormat::Mov => "mov",
            OutputFormat::Flv => "flv",
        }
    }
}

impl From<&str> for OutputFormat {
    fn from(s: &str) -> Self {
        match s.to_lowercase().as_str() {
            "mp4" => OutputFormat::Mp4,
            "mkv" => OutputFormat::Mkv,
            "mov" => OutputFormat::Mov,
            "flv" => OutputFormat::Flv,
            _ => OutputFormat::Mp4,
        }
    }
}

/// Recording statistics.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RecordingStats {
    /// Total frames recorded.
    pub frames_recorded: u64,
    /// Frames dropped due to encoding lag.
    pub frames_dropped: u64,
    /// Average frame time in milliseconds.
    pub avg_frame_time_ms: f64,
    /// Current bitrate in kbps.
    pub current_bitrate: u32,
    /// Recording duration in seconds.
    pub duration_secs: f64,
}

impl RecordingStats {
    /// Calculate the drop rate percentage.
    pub fn drop_rate(&self) -> f64 {
        if self.frames_recorded == 0 {
            return 0.0;
        }
        (self.frames_dropped as f64 / self.frames_recorded as f64) * 100.0
    }

    /// Check if the recording is healthy (low drop rate).
    pub fn is_healthy(&self) -> bool {
        self.drop_rate() < 5.0 // Less than 5% dropped frames
    }
}

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

    #[test]
    fn test_output_format_from_str() {
        assert_eq!(OutputFormat::from("mp4"), OutputFormat::Mp4);
        assert_eq!(OutputFormat::from("MKV"), OutputFormat::Mkv);
        assert_eq!(OutputFormat::from("unknown"), OutputFormat::Mp4);
    }

    #[test]
    fn test_recording_result_duration_human() {
        let result = RecordingResult {
            path: PathBuf::from("/tmp/test.mp4"),
            game_id: Some(12345),
            champion: Some("Ahri".to_string()),
            start_time: Utc::now(),
            end_time: Utc::now() + Duration::seconds(125),
            duration: Duration::seconds(125),
            stats: None,
        };

        assert_eq!(result.duration_human(), "2m 5s");
    }

    #[test]
    fn test_recording_stats_drop_rate() {
        let stats = RecordingStats {
            frames_recorded: 1000,
            frames_dropped: 40,
            ..Default::default()
        };

        assert_eq!(stats.drop_rate(), 4.0);
        assert!(stats.is_healthy());
    }
}
