use std::fmt;

use serde::{de, Serialize, Deserializer, Serializer};
use tracing::{level_filters::LevelFilter, Level};

pub fn serialize<S: Serializer>(level: &Option<Level>, s: S) -> Result<S::Ok, S::Error> {
    LevelFilter::from(*level).to_string().serialize(s)
}

pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<Option<Level>, D::Error> {
    struct Visitor;

    const E: &str = r#"one of "off", "error", "warn", "info", "debug", "trace", or 0-5"#;

    impl<'de> de::Visitor<'de> for Visitor {
        type Value = Option<Level>;

        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "expected {E}")
        }

        fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
            v.try_into()
                .map_err(|_| E::invalid_value(de::Unexpected::Signed(v), &E))
                .and_then(|v| self.visit_u64(v))
        }

        fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
            let filter = match v {
                0 => LevelFilter::OFF,
                1 => LevelFilter::ERROR,
                2 => LevelFilter::WARN,
                3 => LevelFilter::INFO,
                4 => LevelFilter::DEBUG,
                5 => LevelFilter::TRACE,
                _ => return Err(E::invalid_value(de::Unexpected::Unsigned(v), &E)),
            };

            Ok(filter.into_level())
        }

        fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
            v.parse::<LevelFilter>()
                .map(|f| f.into_level())
                .map_err(|_| E::invalid_value(de::Unexpected::Str(v), &E))
        }
    }

    de.deserialize_map(Visitor)
}
