use serde::{Deserialize, Serialize};

/// Audio format options for downloads
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum AudioFormat {
    #[serde(rename = "mp3")]
    Mp3,
    #[serde(rename = "wav")]
    Wav,
    #[serde(rename = "stem")]
    Stem,
}

impl std::fmt::Display for AudioFormat {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AudioFormat::Mp3 => write!(f, "mp3"),
            AudioFormat::Wav => write!(f, "wav"),
            AudioFormat::Stem => write!(f, "stem"),
        }
    }
}

impl AudioFormat {
    pub fn extension(&self) -> &'static str {
        match self {
            AudioFormat::Mp3 => "mp3",
            AudioFormat::Wav => "wav",
            AudioFormat::Stem => "zip",
        }
    }

    pub fn mime_type(&self) -> &'static str {
        match self {
            AudioFormat::Mp3 => "audio/mpeg",
            AudioFormat::Wav => "audio/wav",
            AudioFormat::Stem => "application/zip",
        }
    }
}

/// Request for audio download
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioDownloadRequest {
    pub clip_id: String,
    pub format: AudioFormat,
}

impl AudioDownloadRequest {
    pub fn new(clip_id: impl Into<String>, format: AudioFormat) -> Self {
        Self {
            clip_id: clip_id.into(),
            format,
        }
    }
}

/// Audio URL information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioUrlInfo {
    pub url: String,
    pub format: AudioFormat,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub size_bytes: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expires_in: Option<i32>,
}

impl AudioUrlInfo {
    pub fn new(url: impl Into<String>, format: AudioFormat) -> Self {
        Self {
            url: url.into(),
            format,
            size_bytes: None,
            expires_in: None,
        }
    }

    pub fn with_size(mut self, size: u64) -> Self {
        self.size_bytes = Some(size);
        self
    }

    pub fn with_expires_in(mut self, seconds: i32) -> Self {
        self.expires_in = Some(seconds);
        self
    }
}

/// Response containing multiple audio URL formats
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioDownloadResponse {
    pub clip_id: String,
    pub urls: Vec<AudioUrlInfo>,
}

impl AudioDownloadResponse {
    pub fn new(clip_id: impl Into<String>) -> Self {
        Self {
            clip_id: clip_id.into(),
            urls: Vec::new(),
        }
    }

    pub fn add_url(mut self, url: AudioUrlInfo) -> Self {
        self.urls.push(url);
        self
    }

    pub fn get_url(&self, format: AudioFormat) -> Option<&AudioUrlInfo> {
        self.urls.iter().find(|u| u.format == format)
    }

    pub fn get_mp3(&self) -> Option<&AudioUrlInfo> {
        self.get_url(AudioFormat::Mp3)
    }

    pub fn get_wav(&self) -> Option<&AudioUrlInfo> {
        self.get_url(AudioFormat::Wav)
    }

    pub fn get_stem(&self) -> Option<&AudioUrlInfo> {
        self.get_url(AudioFormat::Stem)
    }
}

/// Direct MP3 stream response (just the bytes)
#[derive(Debug, Clone)]
pub struct AudioStream {
    pub bytes: bytes::Bytes,
    pub format: AudioFormat,
    pub content_type: String,
}

impl AudioStream {
    pub fn new(bytes: bytes::Bytes, format: AudioFormat) -> Self {
        Self {
            bytes,
            content_type: format.mime_type().to_string(),
            format,
        }
    }
}

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

    #[test]
    fn test_audio_format_display() {
        assert_eq!(AudioFormat::Mp3.to_string(), "mp3");
        assert_eq!(AudioFormat::Wav.to_string(), "wav");
        assert_eq!(AudioFormat::Stem.to_string(), "stem");
    }

    #[test]
    fn test_audio_format_extension() {
        assert_eq!(AudioFormat::Mp3.extension(), "mp3");
        assert_eq!(AudioFormat::Wav.extension(), "wav");
        assert_eq!(AudioFormat::Stem.extension(), "zip");
    }

    #[test]
    fn test_audio_format_mime_type() {
        assert_eq!(AudioFormat::Mp3.mime_type(), "audio/mpeg");
        assert_eq!(AudioFormat::Wav.mime_type(), "audio/wav");
        assert_eq!(AudioFormat::Stem.mime_type(), "application/zip");
    }

    #[test]
    fn test_audio_url_info_builder() {
        let info = AudioUrlInfo::new("https://example.com/audio.mp3", AudioFormat::Mp3)
            .with_size(1024000)
            .with_expires_in(3600);

        assert_eq!(info.url, "https://example.com/audio.mp3");
        assert_eq!(info.format, AudioFormat::Mp3);
        assert_eq!(info.size_bytes, Some(1024000));
        assert_eq!(info.expires_in, Some(3600));
    }

    #[test]
    fn test_audio_download_response_get_url() {
        let mut response = AudioDownloadResponse::new("clip-123");
        response = response.add_url(AudioUrlInfo::new("https://example.com/audio.mp3", AudioFormat::Mp3));
        response = response.add_url(AudioUrlInfo::new("https://example.com/audio.wav", AudioFormat::Wav));

        assert!(response.get_mp3().is_some());
        assert!(response.get_wav().is_some());
        assert!(response.get_stem().is_none());
    }
}
