use std::str::FromStr;

use serde::{Deserialize, Serialize};

use crate::UtilesError;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
#[serde(rename_all = "kebab-case")]
#[derive(Default)]
pub enum HashType {
    Md5,
    Fnv1a,
    Xxh32,
    #[default]
    Xxh64,
    Xxh3_64,
    Xxh3_128,
}

impl std::fmt::Display for HashType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Md5 => write!(f, "md5"),
            Self::Fnv1a => write!(f, "fnv1a"),
            Self::Xxh32 => write!(f, "xxh32"),
            Self::Xxh64 => write!(f, "xxh64"),
            Self::Xxh3_64 => write!(f, "xxh3_64"),
            Self::Xxh3_128 => write!(f, "xxh3_128"),
        }
    }
}

impl FromStr for HashType {
    type Err = UtilesError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_ascii_lowercase().as_str() {
            "md5" => Ok(Self::Md5),
            "fnv" | "fnv1a" | "fnv1a64" => Ok(Self::Fnv1a),
            "xxh32" => Ok(Self::Xxh32),
            "xxh64" => Ok(Self::Xxh64),
            "xxh3" | "xxh3_64" | "xxh3-64" => Ok(Self::Xxh3_64),
            "xxh3_128" | "xxh3-128" => Ok(Self::Xxh3_128),
            _ => Err(UtilesError::Error(format!(
                "HashType::from_str: unknown hash type: {s}"
            ))),
        }
    }
}

impl HashType {
    #[must_use]
    pub const fn sqlite_fn_name(&self) -> &'static str {
        match self {
            Self::Md5 => "md5",
            Self::Fnv1a => "fnv1a",
            Self::Xxh32 => "xxh32",
            Self::Xxh64 => "xxh64",
            Self::Xxh3_64 => "xxh3_64",
            Self::Xxh3_128 => "xxh3_128",
        }
    }

    #[must_use]
    pub const fn sqlite_hex_fn_name(&self) -> &'static str {
        match self {
            Self::Md5 => "md5_hex",
            Self::Fnv1a => "fnv1a_hex",
            Self::Xxh32 => "xxh32_hex",
            Self::Xxh64 => "xxh64_hex",
            Self::Xxh3_64 => "xxh3_64_hex",
            Self::Xxh3_128 => "xxh3_128_hex",
        }
    }

    #[must_use]
    pub const fn digest_size(&self) -> usize {
        match self {
            Self::Xxh32 => 4,
            Self::Md5 | Self::Xxh3_128 => 16,
            Self::Fnv1a | Self::Xxh64 | Self::Xxh3_64 => 8,
        }
    }
}
