use std::{
    fmt::{self, Display},
    str::FromStr,
};
use serde::Deserialize;
#[derive(Debug, thiserror::Error)]
pub enum PrecisionError {
    #[error("invalid precision repr: {0}")]
    Invalid(String),
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde_repr::Serialize_repr)]
pub enum Precision {
    Millisecond = 0,
    Microsecond,
    Nanosecond,
}
impl Default for Precision {
    fn default() -> Self {
        Self::Millisecond
    }
}
impl PartialEq<str> for Precision {
    fn eq(&self, other: &str) -> bool {
        self.as_str() == other
    }
}
impl PartialEq<&str> for Precision {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}
impl Precision {
    pub const fn as_str(&self) -> &'static str {
        use Precision::*;
        match self {
            Millisecond => "ms",
            Microsecond => "us",
            Nanosecond => "ns",
        }
    }
    pub const fn as_u8(&self) -> u8 {
        match self {
            Self::Millisecond => 0,
            Self::Microsecond => 1,
            Self::Nanosecond => 2,
        }
    }
    pub const fn from_u8(precision: u8) -> Self {
        match precision {
            0 => Self::Millisecond,
            1 => Self::Microsecond,
            2 => Self::Nanosecond,
            _ => panic!("precision integer only allow 0/1/2"),
        }
    }
    pub const fn to_seconds_format(self) -> chrono::SecondsFormat {
        match self {
            Precision::Millisecond => chrono::SecondsFormat::Millis,
            Precision::Microsecond => chrono::SecondsFormat::Micros,
            Precision::Nanosecond => chrono::SecondsFormat::Nanos,
        }
    }
}
macro_rules! _impl_from {
    ($($ty:ty) *) => {
        $(impl From<$ty> for Precision {
            fn from(v: $ty) -> Self {
                Self::from_u8(v as _)
            }
        })*
    }
}
_impl_from!(i8 i16 i32 i64 isize u8 u16 u32 u64 usize);
impl Display for Precision {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}
impl FromStr for Precision {
    type Err = PrecisionError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ms" => Ok(Precision::Millisecond),
            "us" => Ok(Precision::Microsecond),
            "ns" => Ok(Precision::Nanosecond),
            s => Err(PrecisionError::Invalid(s.to_string())),
        }
    }
}
impl<'de> Deserialize<'de> for Precision {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct PrecisionVisitor;
        impl<'de> serde::de::Visitor<'de> for PrecisionVisitor {
            type Value = Precision;
            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("expect integer 0/1/2 or string ms/us/ns")
            }
            fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_i32(v as _)
            }
            fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_i32(v as _)
            }
            fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Precision::try_from(v).map_err(<E as serde::de::Error>::custom)
            }
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_i32(v as _)
            }
            fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_i32(v as _)
            }
            fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_i32(v as _)
            }
            fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_i32(v as _)
            }
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_i32(v as _)
            }
            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Precision::from_str(v).map_err(<E as serde::de::Error>::custom)
            }
            fn visit_none<E>(self) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Ok(Precision::Millisecond)
            }
            fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
            where
                D: serde::Deserializer<'de>,
            {
                deserializer.deserialize_any(self)
            }
            fn visit_unit<E>(self) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                Ok(Precision::Millisecond)
            }
        }
        deserializer.deserialize_any(PrecisionVisitor)
    }
}
#[cfg(test)]
mod tests {
    use serde::{
        de::{
            value::{I32Deserializer, StrDeserializer, UnitDeserializer},
            IntoDeserializer,
        },
        Deserialize,
    };
    use super::Precision;
    #[test]
    fn de() {
        type SD<'a> = StrDeserializer<'a, serde::de::value::Error>;
        let precision = Precision::deserialize::<SD>("ms".into_deserializer()).unwrap();
        assert_eq!(precision, Precision::Millisecond);
        let precision = Precision::deserialize::<SD>("us".into_deserializer()).unwrap();
        assert_eq!(precision, Precision::Microsecond);
        let precision = Precision::deserialize::<SD>("ns".into_deserializer()).unwrap();
        assert_eq!(precision, Precision::Nanosecond);
        type I32D = I32Deserializer<serde::de::value::Error>;
        let precision = Precision::deserialize::<I32D>(0.into_deserializer()).unwrap();
        assert_eq!(precision, Precision::Millisecond);
        let precision = Precision::deserialize::<I32D>(1.into_deserializer()).unwrap();
        assert_eq!(precision, Precision::Microsecond);
        let precision = Precision::deserialize::<I32D>(2.into_deserializer()).unwrap();
        assert_eq!(precision, Precision::Nanosecond);
        type UnitD = UnitDeserializer<serde::de::value::Error>;
        let precision = Precision::deserialize::<UnitD>(().into_deserializer()).unwrap();
        assert_eq!(precision, Precision::Millisecond);
        let json = serde_json::to_string(&precision).unwrap();
        assert_eq!(json, "0");
    }
}