use std::{collections::HashMap, marker::PhantomData};
use bytes::Bytes;
use chrono::FixedOffset;
use serde::{
    de::{Error, SeqAccess, Unexpected, Visitor},
    Deserialize, Deserializer,
};
use crate::{
    types::{
        BoltBoolean, BoltBytes, BoltFloat, BoltInteger, BoltList, BoltMap, BoltNull, BoltString,
        BoltType,
    },
    EndNodeId, Id, Indices, Keys, Labels, Nodes, Offset, Relationships, StartNodeId, Timezone,
    Type,
};
impl<'de> Deserialize<'de> for BoltString {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        String::deserialize(deserializer).map(|value| BoltString { value })
    }
}
impl<'de> Deserialize<'de> for BoltBoolean {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        bool::deserialize(deserializer).map(|value| BoltBoolean { value })
    }
}
impl<'de> Deserialize<'de> for BoltMap {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        HashMap::<BoltString, BoltType>::deserialize(deserializer).map(|value| BoltMap { value })
    }
}
impl<'de> Deserialize<'de> for BoltNull {
    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(BoltNull {})
    }
}
impl<'de> Deserialize<'de> for BoltInteger {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        i64::deserialize(deserializer).map(|value| BoltInteger { value })
    }
}
impl<'de> Deserialize<'de> for BoltFloat {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        f64::deserialize(deserializer).map(|value| BoltFloat { value })
    }
}
impl<'de> Deserialize<'de> for BoltList {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Vec::<BoltType>::deserialize(deserializer).map(|value| BoltList { value })
    }
}
impl<'de> Deserialize<'de> for BoltBytes {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Bytes::deserialize(deserializer).map(|value| BoltBytes { value })
    }
}
macro_rules! newtype_deser {
    ($($outer:ident$(<$param:ident>)?($inner:ty) => $typ:ty),+ $(,)?) => {
        $(
            impl<'de$(, $param: Deserialize<'de>)?> Deserialize<'de> for $typ {
                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
                where
                    D: Deserializer<'de>,
                {
                    struct TheVisitor$(<$param>(PhantomData<$param>))?;
                    impl<'de$(, $param: Deserialize<'de>)?> Visitor<'de> for TheVisitor$(<$param>)? {
                        type Value = $typ;
                        fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                            formatter.write_str(concat!("newtype struct ", stringify!($outer)))
                        }
                        fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
                        where
                            D: Deserializer<'de>,
                        {
                            let value = <$inner>::deserialize(deserializer)?;
                            Ok($outer(value))
                        }
                        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
                        where
                            A: SeqAccess<'de>,
                        {
                            seq.next_element::<$inner>()?
                                .ok_or_else(|| Error::invalid_length(0, &self))
                                .map($outer)
                        }
                    }
                    deserializer.deserialize_newtype_struct(stringify!($outer), TheVisitor$((PhantomData::<$param>))?)
                }
            }
        )+
    };
}
newtype_deser!(
    Id(u64) => Id,
    StartNodeId(u64) => StartNodeId,
    EndNodeId(u64) => EndNodeId,
    Labels<Coll>(Coll) => Labels<Coll>,
    Type<T>(T) => Type<T>,
    Keys<Coll>(Coll) => Keys<Coll>,
    Timezone<T>(T) => Timezone<T>,
    Nodes<T>(Vec<T>) => Nodes<T>,
    Relationships<T>(Vec<T>) => Relationships<T>,
    Indices<T>(Vec<T>) => Indices<T>,
);
impl<'de> Deserialize<'de> for Offset<FixedOffset> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        fn try_to_offset<T>(v: T) -> Result<FixedOffset, Unexpected<'static>>
        where
            T: Copy + TryInto<i32> + TryInto<i64> + TryInto<u64>,
        {
            match v.try_into().ok().and_then(FixedOffset::east_opt) {
                Some(offset) => Ok(offset),
                None => match v.try_into() {
                    Ok(v) => Err(Unexpected::Signed(v)),
                    Err(_) => match v.try_into() {
                        Ok(v) => Err(Unexpected::Unsigned(v)),
                        Err(_) => Err(Unexpected::Other("big number")),
                    },
                },
            }
        }
        struct OffsetVisitor;
        impl<'de> Visitor<'de> for OffsetVisitor {
            type Value = FixedOffset;
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("an offset value as i32 within [-86_400, 86_400]")
            }
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
            where
                E: Error,
            {
                try_to_offset(v).map_err(|u| Error::invalid_value(u, &self))
            }
            fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
            where
                E: Error,
            {
                try_to_offset(v).map_err(|u| Error::invalid_value(u, &self))
            }
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
            where
                E: Error,
            {
                try_to_offset(v).map_err(|u| Error::invalid_value(u, &self))
            }
            fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
            where
                E: Error,
            {
                try_to_offset(v).map_err(|u| Error::invalid_value(u, &self))
            }
        }
        deserializer.deserialize_i32(OffsetVisitor).map(Offset)
    }
}