use core::fmt;

use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use serde::de;
use serde::de::Visitor;

/// Represents a min cardinality which must be a 0 or positive integer.
#[derive(PartialEq, Eq, Hash, PartialOrd, Debug, Clone, Copy)]
pub struct Min {
    pub value: usize,
}

impl Min {
    pub fn is_0(&self) -> bool {
        self.value == 0
    }
}

impl From<usize> for Min {
    fn from(v: usize) -> Self {
        Min { value: v }
    }
}

impl From<i32> for Min {
    fn from(v: i32) -> Self {
        Min { value: v as usize }
    }
}

impl From<i8> for Min {
    fn from(v: i8) -> Self {
        Min { value: v as usize }
    }
}

impl From<i64> for Min {
    fn from(v: i64) -> Self {
        Min { value: v as usize }
    }
}

impl From<u64> for Min {
    fn from(v: u64) -> Self {
        Min { value: v as usize }
    }
}

impl Serialize for Min {
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
    where
        S: Serializer,
    {
        let value = self.value as u64;
        serializer.serialize_u64(value)
    }
}

struct MinVisitor;

impl Visitor<'_> for MinVisitor {
    type Value = Min;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a positive integer")
    }

    fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        if value < -1 {
            Err(E::custom(format!("value of type i8 {value} should be -1 or positive")))
        } else {
            let n = Min::from(value);
            Ok(n)
        }
    }

    fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        if value < -1 {
            Err(E::custom(format!("value of type i32 {value} should be -1 or positive")))
        } else {
            Ok(Min::from(value))
        }
    }

    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        if value < -1 {
            Err(E::custom(format!("value of type i64 {value} should be -1 or positive")))
        } else {
            Ok(Min::from(value))
        }
    }

    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(Min::from(value))
    }
}

impl<'de> Deserialize<'de> for Min {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_u64(MinVisitor)
    }
}

#[cfg(test)]
mod tests {
    // use serde_json::*;
    // use serde::Serialize;

    use crate::Min;

    #[test]
    fn test_serialize_min() {
        let min = Min::from(23);
        let str = serde_json::to_string(&min).unwrap();
        assert_eq!(str, "23");
    }

    #[test]
    fn test_deserialize_min() {
        let min = Min::from(23);
        let min_deser = serde_json::from_str("23").unwrap();
        assert_eq!(min, min_deser);
    }
}
