use std::borrow::Cow;
use std::num::NonZeroU32;

use serde::{Deserialize, Deserializer, Serialize};

/// Prevents passing raw strings where model IDs are expected.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct ModelId(Cow<'static, str>);

impl ModelId {
    pub const fn borrowed(id: &'static str) -> Self {
        Self(Cow::Borrowed(id))
    }

    pub fn new(id: impl Into<Cow<'static, str>>) -> Self {
        Self(id.into())
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl<'de> Deserialize<'de> for ModelId {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        String::deserialize(deserializer).map(|s| Self(Cow::Owned(s)))
    }
}

impl std::fmt::Display for ModelId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.0)
    }
}

/// Prevents passing raw strings where engine names are expected.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct EngineName(Cow<'static, str>);

impl EngineName {
    pub const fn borrowed(name: &'static str) -> Self {
        Self(Cow::Borrowed(name))
    }

    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
        Self(name.into())
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl<'de> Deserialize<'de> for EngineName {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        String::deserialize(deserializer).map(|s| Self(Cow::Owned(s)))
    }
}

impl std::fmt::Display for EngineName {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&self.0)
    }
}

/// Prevents mixing up bytes, megabytes, and gigabytes.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Megabytes(pub u64);

impl Megabytes {
    pub const fn from_gb(gb: u64) -> Self {
        Self(gb.saturating_mul(1024))
    }

    pub const fn from_mb(mb: u64) -> Self {
        Self(mb)
    }

    pub fn as_gb(&self) -> f64 {
        self.0 as f64 / 1024.0
    }
}

impl std::fmt::Display for Megabytes {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.0 >= 1024 {
            write!(f, "{:.1} GB", self.as_gb())
        } else {
            write!(f, "{} MB", self.0)
        }
    }
}

/// Wraps raw audio samples with metadata.
#[derive(Debug, Clone)]
pub struct AudioSamples {
    samples: Vec<f32>,
    sample_rate: NonZeroU32,
    channels: u16,
}

impl AudioSamples {
    pub fn new(samples: Vec<f32>, sample_rate: u32, channels: u16) -> Self {
        Self::try_new(samples, sample_rate, channels)
            .expect("AudioSamples sample_rate must be non-zero")
    }

    pub fn try_new(
        samples: Vec<f32>,
        sample_rate: u32,
        channels: u16,
    ) -> std::result::Result<Self, &'static str> {
        let Some(sample_rate) = NonZeroU32::new(sample_rate) else {
            return Err("sample_rate must be non-zero");
        };
        Ok(Self {
            samples,
            sample_rate,
            channels,
        })
    }

    pub fn mono_16khz(samples: Vec<f32>) -> Self {
        Self {
            samples,
            sample_rate: NonZeroU32::new(crate::constants::WHISPER_SAMPLE_RATE)
                .expect("WHISPER_SAMPLE_RATE must be non-zero"),
            channels: crate::constants::WHISPER_CHANNELS,
        }
    }

    pub fn samples(&self) -> &[f32] {
        &self.samples
    }

    pub fn into_samples(self) -> Vec<f32> {
        self.samples
    }

    pub fn sample_rate(&self) -> u32 {
        self.sample_rate.get()
    }

    pub fn channels(&self) -> u16 {
        self.channels
    }

    pub fn duration_secs(&self) -> f64 {
        self.samples.len() as f64 / self.sample_rate.get() as f64
    }
}

/// A single timed segment of a transcription.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Segment {
    pub start: f64,
    pub end: f64,
    pub text: String,
}

/// The result of a transcription.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Transcript {
    segments: Vec<Segment>,
    language: String,
    duration: f64,
}

impl Transcript {
    pub fn new(segments: Vec<Segment>, language: String, duration: f64) -> Self {
        Self {
            segments,
            language,
            duration,
        }
    }

    pub fn text(&self) -> String {
        self.segments
            .iter()
            .map(|s| s.text.as_str())
            .collect::<Vec<_>>()
            .join(" ")
    }

    pub fn segments(&self) -> &[Segment] {
        &self.segments
    }

    pub fn language(&self) -> &str {
        &self.language
    }

    pub fn duration(&self) -> f64 {
        self.duration
    }
}

/// Options passed to a transcription engine.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TranscriptionOptions {
    pub language: Option<String>,
    pub initial_prompt: Option<String>,
}

/// Progress update during model download.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DownloadProgress {
    pub model_id: ModelId,
    pub file_name: String,
    pub bytes_downloaded: u64,
    pub total_bytes: u64,
    pub percent: u8,
}
