use std::str::FromStr;

#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, derive_more::Display, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, strum::AsRefStr)]
#[repr(u8)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum Text {
    #[display("application/pgp-signature")]
    PgpSignature,
    #[display("application/yml; charset=utf-8")]
    Yml,
    #[display("text/html; charset=utf-8")]
    Html,
    #[display("application/json; charset=utf-8")]
    Json,
    #[display("application/javascript; charset=utf-8")]
    Js,
    #[display("text/plain; charset=utf-8")]
    Txt,
    #[display("text/markdown; charset=utf-8")]
    Markdown,
    #[display("text/css; charset=utf-8")]
    Css,
}

// Evil shit 😈
// unsafe impl bytemuck::Zeroable for Text {}
// unsafe impl bytemuck::Pod for Text {}

#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, derive_more::Display, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, strum::AsRefStr)]
#[repr(u8)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum Video {
    #[display("video/mp4")]
    Mp4,
    #[display("video/webm")]
    Webm,
}

// Evil shit 😈
// unsafe impl Zeroable for Video {}
// unsafe impl Pod for Video {}

#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, derive_more::Display, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, strum::AsRefStr)]
#[repr(u8)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum Audio {
    #[display("audio/flac")]
    Flac,
    #[display("audio/mpeg")]
    Mpeg,
    #[display("audio/wav")]
    Wav,
    #[display(" audio/mp4")]
    Mp4,
}

// Evil shit 😈
// unsafe impl Zeroable for Audio {}
// unsafe impl Pod for Audio {}

#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, derive_more::Display, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, strum::AsRefStr)]
#[repr(u8)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum Image {
    #[display("image/apng")]
    Apng,
    #[display("image/avif")]
    Avif,
    #[display("image/gif")]
    Gif,
    #[display("image/jpeg")]
    Jpeg,
    #[display("image/jpg")]
    Jpg,
    #[display("image/png")]
    Png,
    #[display("image/webp")]
    Webp,
    #[display("image/svg+xml")]
    Svg,
}

// Evil shit 😈
// unsafe impl Zeroable for Image {}
// unsafe impl Pod for Image {}

#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, derive_more::Display, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, strum::AsRefStr)]
#[repr(u8)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum Doc {
    #[display("application/pdf")]
    Pdf,
}

// Evil shit 😈
// unsafe impl Zeroable for Doc {}
// unsafe impl Pod for Doc {}

#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, derive_more::Display, Debug, Clone, Copy, serde::Serialize, serde::Deserialize, strum::AsRefStr)]
#[repr(u8)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum Other {
    #[display("application/x-msdownload")]
    Exe,
    #[display("model/gltf-binary")]
    Glb,
    #[display("model/stl")]
    Stl,
    #[display("application/octet-stream")]
    Bytes,
}

// Evil shit 😈
// unsafe impl Zeroable for Other {}
// unsafe impl Pod for Other {}

#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum Extension {
    Text(Text) = 1,
    Video(Video) = 2,
    Audio(Audio) = 3,
    Image(Image) = 4,
    Doc(Doc) = 5,
    Other(Other) = 6,
}

impl Extension {
    pub fn file_type(&self) -> String {
        let extension = match self {
            Extension::Text(v) => v.as_ref().to_string(),
            Extension::Video(v) => v.as_ref().to_string(),
            Extension::Audio(v) => v.as_ref().to_string(),
            Extension::Image(v) => v.as_ref().to_string(),
            Extension::Doc(v) => v.as_ref().to_string(),
            Extension::Other(v) => v.as_ref().to_string(),
        };

        extension.to_uppercase()
    }
}

impl std::fmt::Display for Extension {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let res = match self {
            Extension::Text(v) => v.to_string(),
            Extension::Video(v) => v.to_string(),
            Extension::Audio(v) => v.to_string(),
            Extension::Image(v) => v.to_string(),
            Extension::Doc(v) => v.to_string(),
            Extension::Other(v) => v.to_string(),
        };
        write!(f, "{}", res)
    }
}

impl FromStr for Extension {
    type Err = anyhow::Error;

    fn from_str(content_type: &str) -> Result<Self, Self::Err> {
        let mime = content_type.trim().replace(' ', "").to_lowercase();

        let extension = match mime.as_str() {
            title if title.starts_with("application/json") => Extension::Text(Text::Json),

            "application/pgp-signature" => Extension::Text(Text::PgpSignature),
            "application/yaml" | "application/yml" => Extension::Text(Text::Yml),

            title if title.starts_with("application/javascript") => Extension::Text(Text::Js),
            title if title.starts_with("text/html") => Extension::Text(Text::Html),
            title if title.starts_with("text/markdown") => Extension::Text(Text::Markdown),
            title if title.starts_with("text/css") => Extension::Text(Text::Css),

            "application/pdf" => Extension::Doc(Doc::Pdf),
            "application/x-msdownload" => Extension::Other(Other::Exe),

            "audio/flac" => Extension::Audio(Audio::Flac),
            "audio/mpeg" => Extension::Audio(Audio::Mpeg),
            "audio/wav" => Extension::Audio(Audio::Wav),
            "audio/mp4" => Extension::Audio(Audio::Mp4),

            "image/apng" => Extension::Image(Image::Apng),
            "image/avif" => Extension::Image(Image::Avif),
            "image/gif" => Extension::Image(Image::Gif),
            "image/jpeg" => Extension::Image(Image::Jpeg),
            "image/jpg" => Extension::Image(Image::Jpg),
            "image/png" => Extension::Image(Image::Png),
            "image/webp" => Extension::Image(Image::Webp),
            "image/svg+xml" => Extension::Image(Image::Svg),

            "video/mp4" => Extension::Video(Video::Mp4),
            "video/webm" => Extension::Video(Video::Webm),
            title if title.starts_with("text") => Extension::Text(Text::Txt),
            gltf if gltf.starts_with("model/gltf") => Extension::Other(Other::Glb),
            stf if stf.starts_with("model/stl") => Extension::Other(Other::Stl),
            _ => Extension::Other(Other::Bytes),
        };

        Ok(extension)
    }
}

// Evil shit 😈
// unsafe impl Zeroable for Extension {}
// unsafe impl Pod for Extension {}
