use newtype::NewType;
use serde::Deserialize;

/// Macro to define a number type with serde support
#[macro_export]
macro_rules! define_num_serde {
    ($name:ident, $inner_type:ty) => {
        #[derive(NewType, Clone, Deserialize, Copy)]
        pub struct $name<const N: $inner_type>(pub $inner_type);

        impl<const N: $inner_type> ::std::fmt::Debug for $name<N> {
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                self.0.fmt(f)
            }
        }

        impl<const N: $inner_type> PartialEq for $name<N> {
            fn eq(&self, other: &Self) -> bool {
                self.0 == other.0
            }
        }

        impl<const N: $inner_type> PartialEq<$inner_type> for $name<N> {
            fn eq(&self, other: &$inner_type) -> bool {
                &self.0 == other
            }
        }

        impl<const N: $inner_type> Eq for $name<N> {}

        impl<const N: $inner_type> PartialOrd for $name<N> {
            fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
                Some(self.cmp(other))
            }
        }

        impl<const N: $inner_type> PartialOrd<$inner_type> for $name<N> {
            fn partial_cmp(&self, other: &$inner_type) -> ::std::option::Option<::std::cmp::Ordering> {
                self.0.partial_cmp(other)
            }
        }

        impl<const N: $inner_type> Ord for $name<N> {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                self.0.cmp(&other.0)
            }
        }

        impl<const N: $inner_type> Default for $name<N> {
            fn default() -> Self {
                Self(N)
            }
        }

        impl<const N: $inner_type> ::std::fmt::Display for $name<N> {
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                write!(f, "{}", self.0)
            }
        }
    };
}

define_num_serde!(NumberUsize, usize);
define_num_serde!(NumberU32, u32);
define_num_serde!(NumberU64, u64);
define_num_serde!(NumberU16, u16);
define_num_serde!(NumberU8, u8);
define_num_serde!(NumberI32, i32);
define_num_serde!(NumberI64, i64);
define_num_serde!(NumberI16, i16);
define_num_serde!(NumberI8, i8);
define_num_serde!(NumberBool, bool);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_case_number_deserialize() {
        assert_eq!(serde_json::from_str::<NumberUsize<1500>>("123").unwrap(), 123); // eq to inner type
        assert!(serde_json::from_str::<NumberUsize<1500>>("123").unwrap() < 124); // compare

        assert_eq!(
            *serde_json::from_str::<NumberUsize<1500>>("xxx").unwrap_or_default(),
            1500
        );
    }

    #[test]
    fn test_case_number_u32_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberU32<500>>("123").unwrap(), 123);
        assert_eq!(*serde_json::from_str::<NumberU32<500>>("xxx").unwrap_or_default(), 500);
    }

    #[test]
    fn test_case_number_u64_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberU64<5000>>("1234").unwrap(), 1234);
        assert_eq!(
            *serde_json::from_str::<NumberU64<5000>>("xxx").unwrap_or_default(),
            5000
        );
    }

    #[test]
    fn test_case_number_u16_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberU16<300>>("100").unwrap(), 100);
        assert_eq!(*serde_json::from_str::<NumberU16<300>>("xxx").unwrap_or_default(), 300);
    }

    #[test]
    fn test_case_number_u8_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberU8<200>>("50").unwrap(), 50);
        assert_eq!(*serde_json::from_str::<NumberU8<200>>("xxx").unwrap_or_default(), 200);
    }

    #[test]
    fn test_case_number_i32_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberI32<-100>>("42").unwrap(), 42);
        assert_eq!(
            *serde_json::from_str::<NumberI32<-100>>("xxx").unwrap_or_default(),
            -100
        );
    }

    #[test]
    fn test_case_number_i64_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberI64<-5000>>("999").unwrap(), 999);
        assert_eq!(
            *serde_json::from_str::<NumberI64<-5000>>("xxx").unwrap_or_default(),
            -5000
        );
    }

    #[test]
    fn test_case_number_i16_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberI16<-50>>("25").unwrap(), 25);
        assert_eq!(*serde_json::from_str::<NumberI16<-50>>("xxx").unwrap_or_default(), -50);
    }

    #[test]
    fn test_case_number_i8_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberI8<-10>>("5").unwrap(), 5);
        assert_eq!(*serde_json::from_str::<NumberI8<-10>>("xxx").unwrap_or_default(), -10);
    }

    #[test]
    fn test_case_number_bool_deserialize() {
        assert_eq!(*serde_json::from_str::<NumberBool<true>>("false").unwrap(), false);
        assert_eq!(
            *serde_json::from_str::<NumberBool<true>>("xxx").unwrap_or_default(),
            true
        );
    }

    #[test]
    fn test_case_number_display_and_debug() {
        let usize_val = NumberUsize::<1500>::from(123);
        assert_eq!(usize_val.to_string(), "123");
        assert_eq!(format!("{:?}", usize_val), "123");

        let u32_val = NumberU32::<500>::from(100);
        assert_eq!(u32_val.to_string(), "100");
        assert_eq!(format!("{:?}", u32_val), "100");

        let u64_val = NumberU64::<5000>::from(1000);
        assert_eq!(u64_val.to_string(), "1000");
        assert_eq!(format!("{:?}", u64_val), "1000");

        let u16_val = NumberU16::<300>::from(200);
        assert_eq!(u16_val.to_string(), "200");
        assert_eq!(format!("{:?}", u16_val), "200");

        let u8_val = NumberU8::<200>::from(150);
        assert_eq!(u8_val.to_string(), "150");
        assert_eq!(format!("{:?}", u8_val), "150");

        let i32_val = NumberI32::<-100>::from(42);
        assert_eq!(i32_val.to_string(), "42");
        assert_eq!(format!("{:?}", i32_val), "42");

        let i64_val = NumberI64::<-5000>::from(999);
        assert_eq!(i64_val.to_string(), "999");
        assert_eq!(format!("{:?}", i64_val), "999");

        let i16_val = NumberI16::<-50>::from(25);
        assert_eq!(i16_val.to_string(), "25");
        assert_eq!(format!("{:?}", i16_val), "25");

        let i8_val = NumberI8::<-10>::from(5);
        assert_eq!(i8_val.to_string(), "5");
        assert_eq!(format!("{:?}", i8_val), "5");

        let bool_val = NumberBool::<true>::from(false);
        assert_eq!(bool_val.to_string(), "false");
        assert_eq!(format!("{:?}", bool_val), "false");
    }
}
