//! Encoding presets and quality levels for video recording.

use serde::{Deserialize, Serialize};

/// Video encoder preset configuration.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum EncoderPreset {
    /// NVIDIA NVENC hardware encoder.
    Nvenc {
        /// Target bitrate in kbps.
        #[serde(default = "default_nvenc_bitrate")]
        bitrate: u32,
        /// Constant Quality level (lower = better quality).
        #[serde(default = "default_nvenc_cq")]
        cq_level: u32,
        /// Use two-pass encoding.
        #[serde(default = "default_two_pass")]
        two_pass: bool,
    },
    /// AMD AMF hardware encoder.
    Amf {
        /// Target bitrate in kbps.
        #[serde(default = "default_amf_bitrate")]
        bitrate: u32,
        /// Quality preset (speed, balanced, quality).
        #[serde(default = "default_amf_quality")]
        quality: AmfQuality,
    },
    /// Software x264 encoder (fallback).
    X264 {
        /// x264 preset (ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow).
        #[serde(default = "default_x264_preset")]
        preset: String,
        /// Target bitrate in kbps.
        #[serde(default = "default_x264_bitrate")]
        bitrate: u32,
        /// Use constant rate factor instead of bitrate.
        #[serde(default)]
        crf: Option<u32>,
    },
}

impl Default for EncoderPreset {
    fn default() -> Self {
        Self::Nvenc {
            bitrate: default_nvenc_bitrate(),
            cq_level: default_nvenc_cq(),
            two_pass: default_two_pass(),
        }
    }
}

fn default_nvenc_bitrate() -> u32 {
    8000
}

fn default_nvenc_cq() -> u32 {
    20
}

fn default_two_pass() -> bool {
    true
}

fn default_amf_bitrate() -> u32 {
    8000
}

fn default_amf_quality() -> AmfQuality {
    AmfQuality::Balanced
}

fn default_x264_preset() -> String {
    "veryfast".to_string()
}

fn default_x264_bitrate() -> u32 {
    6000
}

/// AMD AMF quality preset.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "lowercase")]
pub enum AmfQuality {
    Speed,
    #[default]
    Balanced,
    Quality,
}

/// Quality level presets that configure resolution and other parameters.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "lowercase")]
pub enum QualityLevel {
    /// 720p @ 30fps, lower bitrate.
    Low,
    /// 1080p @ 30fps, moderate bitrate.
    Medium,
    /// 1080p @ 60fps, high bitrate.
    #[default]
    High,
    /// 1440p @ 60fps, maximum quality.
    Ultra,
}

impl QualityLevel {
    /// Get the target resolution for this quality level.
    pub fn resolution(&self) -> (u32, u32) {
        match self {
            QualityLevel::Low => (1280, 720),
            QualityLevel::Medium => (1920, 1080),
            QualityLevel::High => (1920, 1080),
            QualityLevel::Ultra => (2560, 1440),
        }
    }

    /// Get the recommended frame rate for this quality level.
    pub fn frame_rate(&self) -> u32 {
        match self {
            QualityLevel::Low => 30,
            QualityLevel::Medium => 30,
            QualityLevel::High => 60,
            QualityLevel::Ultra => 60,
        }
    }

    /// Get the recommended bitrate in kbps for this quality level.
    pub fn recommended_bitrate(&self) -> u32 {
        match self {
            QualityLevel::Low => 4500,
            QualityLevel::Medium => 6000,
            QualityLevel::High => 8000,
            QualityLevel::Ultra => 12000,
        }
    }
}

fn preset_default_true() -> bool {
    true
}

/// Audio capture settings.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AudioSettings {
    /// Enable audio recording.
    #[serde(default = "preset_default_true")]
    pub enabled: bool,

    /// Audio bitrate in kbps.
    #[serde(default = "default_audio_bitrate")]
    pub bitrate: u32,

    /// Sample rate in Hz.
    #[serde(default = "default_sample_rate")]
    pub sample_rate: u32,

    /// Number of audio channels.
    #[serde(default = "default_channels")]
    pub channels: u32,

    /// Capture game audio.
    #[serde(default = "preset_default_true")]
    pub capture_game: bool,

    /// Capture microphone.
    #[serde(default)]
    pub capture_mic: bool,

    /// Microphone device name (if capture_mic is true).
    #[serde(default)]
    pub mic_device: Option<String>,
}

impl Default for AudioSettings {
    fn default() -> Self {
        Self {
            enabled: true,
            bitrate: default_audio_bitrate(),
            sample_rate: default_sample_rate(),
            channels: default_channels(),
            capture_game: true,
            capture_mic: false,
            mic_device: None,
        }
    }
}

fn default_audio_bitrate() -> u32 {
    192
}

fn default_sample_rate() -> u32 {
    48000
}

fn default_channels() -> u32 {
    2
}

/// Encoder capabilities detection.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EncoderCapability {
    /// NVIDIA NVENC available.
    Nvenc,
    /// AMD AMF available.
    Amf,
    /// Intel QuickSync available.
    QuickSync,
    /// Software encoding only.
    Software,
}

impl EncoderPreset {
    /// Check if this preset uses hardware encoding.
    pub fn is_hardware(&self) -> bool {
        matches!(
            self,
            EncoderPreset::Nvenc { .. } | EncoderPreset::Amf { .. }
        )
    }

    /// Get the encoder name for OBS.
    pub fn encoder_name(&self) -> &'static str {
        match self {
            EncoderPreset::Nvenc { .. } => "jim_nvenc",
            EncoderPreset::Amf { .. } => "amd_amf_h264",
            EncoderPreset::X264 { .. } => "x264",
        }
    }

    /// Get the effective bitrate for this preset.
    pub fn effective_bitrate(&self) -> u32 {
        match self {
            EncoderPreset::Nvenc { bitrate, .. } => *bitrate,
            EncoderPreset::Amf { bitrate, .. } => *bitrate,
            EncoderPreset::X264 { bitrate, .. } => *bitrate,
        }
    }

    /// Create a preset optimized for the given quality level.
    pub fn for_quality(quality: QualityLevel, capability: EncoderCapability) -> Self {
        let bitrate = quality.recommended_bitrate();

        match capability {
            EncoderCapability::Nvenc => EncoderPreset::Nvenc {
                bitrate,
                cq_level: 20,
                two_pass: true,
            },
            EncoderCapability::Amf => EncoderPreset::Amf {
                bitrate,
                quality: AmfQuality::Balanced,
            },
            EncoderCapability::QuickSync => EncoderPreset::X264 {
                preset: "veryfast".to_string(),
                bitrate,
                crf: None,
            },
            EncoderCapability::Software => EncoderPreset::X264 {
                preset: "superfast".to_string(),
                bitrate: (bitrate as f32 * 0.8) as u32,
                crf: Some(23),
            },
        }
    }
}

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

    #[test]
    fn test_quality_level_resolution() {
        assert_eq!(QualityLevel::Low.resolution(), (1280, 720));
        assert_eq!(QualityLevel::High.resolution(), (1920, 1080));
        assert_eq!(QualityLevel::Ultra.resolution(), (2560, 1440));
    }

    #[test]
    fn test_encoder_preset_hardware_detection() {
        let nvenc = EncoderPreset::Nvenc {
            bitrate: 8000,
            cq_level: 20,
            two_pass: true,
        };
        assert!(nvenc.is_hardware());

        let x264 = EncoderPreset::X264 {
            preset: "fast".to_string(),
            bitrate: 6000,
            crf: None,
        };
        assert!(!x264.is_hardware());
    }

    #[test]
    fn test_preset_for_quality() {
        let preset = EncoderPreset::for_quality(QualityLevel::High, EncoderCapability::Nvenc);
        assert_eq!(preset.effective_bitrate(), 8000);
        assert!(preset.is_hardware());
    }
}
