// SPDX-License-Identifier: Apache-2.0

use super::{Error, Integer, Value};

use alloc::{boxed::Box, string::String, vec::Vec};
use core::iter::Peekable;

use ciborium_ll::tag;
use serde::de::{self, Deserializer as _};

impl<'a> From<Integer> for de::Unexpected<'a> {
    #[inline]
    fn from(value: Integer) -> Self {
        u64::try_from(value)
            .map(de::Unexpected::Unsigned)
            .unwrap_or_else(|_| {
                i64::try_from(value)
                    .map(de::Unexpected::Signed)
                    .unwrap_or_else(|_| de::Unexpected::Other("large integer"))
            })
    }
}

impl<'a> From<&'a Value> for de::Unexpected<'a> {
    #[inline]
    fn from(value: &'a Value) -> Self {
        match value {
            Value::Bool(x) => Self::Bool(*x),
            Value::Integer(x) => Self::from(*x),
            Value::Float(x) => Self::Float(*x),
            Value::Bytes(x) => Self::Bytes(x),
            Value::Text(x) => Self::Str(x),
            Value::Array(..) => Self::Seq,
            Value::Map(..) => Self::Map,
            Value::Null => Self::Other("null"),
            Value::Tag(..) => Self::Other("tag"),
        }
    }
}

macro_rules! mkvisit {
    ($($f:ident($v:ty)),+ $(,)?) => {
        $(
            #[inline]
            fn $f<E: de::Error>(self, v: $v) -> Result<Self::Value, E> {
                Ok(v.into())
            }
        )+
    };
}

struct Visitor;

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

    fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(formatter, "a valid CBOR item")
    }

    mkvisit! {
        visit_bool(bool),
        visit_f32(f32),
        visit_f64(f64),

        visit_i8(i8),
        visit_i16(i16),
        visit_i32(i32),
        visit_i64(i64),
        visit_i128(i128),

        visit_u8(u8),
        visit_u16(u16),
        visit_u32(u32),
        visit_u64(u64),
        visit_u128(u128),

        visit_char(char),
        visit_str(&str),
        visit_borrowed_str(&'de str),
        visit_string(String),

        visit_bytes(&[u8]),
        visit_borrowed_bytes(&'de [u8]),
        visit_byte_buf(Vec<u8>),
    }

    #[inline]
    fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
        Ok(Value::Null)
    }

    #[inline]
    fn visit_some<D: de::Deserializer<'de>>(
        self,
        deserializer: D,
    ) -> Result<Self::Value, D::Error> {
        deserializer.deserialize_any(self)
    }

    #[inline]
    fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
        Ok(Value::Null)
    }

    #[inline]
    fn visit_newtype_struct<D: de::Deserializer<'de>>(
        self,
        deserializer: D,
    ) -> Result<Self::Value, D::Error> {
        deserializer.deserialize_any(self)
    }

    #[inline]
    fn visit_seq<A: de::SeqAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> {
        let mut seq = Vec::new();

        while let Some(elem) = acc.next_element()? {
            seq.push(elem);
        }

        Ok(Value::Array(seq))
    }

    #[inline]
    fn visit_map<A: de::MapAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> {
        let mut map = Vec::<(Value, Value)>::new();

        while let Some(kv) = acc.next_entry()? {
            map.push(kv);
        }

        Ok(Value::Map(map))
    }

    #[inline]
    fn visit_enum<A: de::EnumAccess<'de>>(self, acc: A) -> Result<Self::Value, A::Error> {
        use serde::de::VariantAccess;

        struct Inner;

        impl<'de> serde::de::Visitor<'de> for Inner {
            type Value = Value;

            fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
                write!(formatter, "a valid CBOR item")
            }

            #[inline]
            fn visit_seq<A: de::SeqAccess<'de>>(self, mut acc: A) -> Result<Self::Value, A::Error> {
                let tag: u64 = acc
                    .next_element()?
                    .ok_or_else(|| de::Error::custom("expected tag"))?;
                let val = acc
                    .next_element()?
                    .ok_or_else(|| de::Error::custom("expected val"))?;
                Ok(Value::Tag(tag, Box::new(val)))
            }
        }

        let (name, data): (String, _) = acc.variant()?;
        assert_eq!("@@TAGGED@@", name);
        data.tuple_variant(2, Inner)
    }
}

impl<'de> de::Deserialize<'de> for Value {
    #[inline]
    fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        deserializer.deserialize_any(Visitor)
    }
}

struct Deserializer<T>(T);

impl<'a> Deserializer<&'a Value> {
    fn integer<N>(&self, kind: &'static str) -> Result<N, Error>
    where
        N: TryFrom<u128>,
        N: TryFrom<i128>,
    {
        fn raw(value: &Value) -> Result<u128, Error> {
            let mut buffer = 0u128.to_ne_bytes();
            let length = buffer.len();

            let bytes = match value {
                Value::Bytes(bytes) => {
                    // Skip leading zeros...
                    let mut bytes: &[u8] = bytes.as_ref();
                    while bytes.len() > buffer.len() && bytes[0] == 0 {
                        bytes = &bytes[1..];
                    }

                    if bytes.len() > buffer.len() {
                        return Err(de::Error::custom("bigint too large"));
                    }

                    bytes
                }

                _ => return Err(de::Error::invalid_type(value.into(), &"bytes")),
            };

            buffer[length - bytes.len()..].copy_from_slice(bytes);
            Ok(u128::from_be_bytes(buffer))
        }

        let err = || de::Error::invalid_type(self.0.into(), &kind);

        Ok(match self.0 {
            Value::Integer(x) => i128::from(*x).try_into().map_err(|_| err())?,
            Value::Tag(t, v) if *t == tag::BIGPOS => raw(v)?.try_into().map_err(|_| err())?,
            Value::Tag(t, v) if *t == tag::BIGNEG => i128::try_from(raw(v)?)
                .map(|x| x ^ !0)
                .map_err(|_| err())
                .and_then(|x| x.try_into().map_err(|_| err()))?,
            _ => return Err(de::Error::invalid_type(self.0.into(), &"(big)int")),
        })
    }
}

impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> {
    type Error = Error;

    #[inline]
    fn deserialize_any<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            Value::Bytes(x) => visitor.visit_bytes(x),
            Value::Text(x) => visitor.visit_str(x),
            Value::Array(x) => visitor.visit_seq(Deserializer(x.iter())),
            Value::Map(x) => visitor.visit_map(Deserializer(x.iter().peekable())),
            Value::Bool(x) => visitor.visit_bool(*x),
            Value::Null => visitor.visit_none(),

            Value::Tag(t, v) => {
                let parent: Deserializer<&Value> = Deserializer(v);
                let access = crate::tag::TagAccess::new(parent, Some(*t));
                visitor.visit_enum(access)
            }

            Value::Integer(x) => {
                if let Ok(x) = u64::try_from(*x) {
                    visitor.visit_u64(x)
                } else if let Ok(x) = i64::try_from(*x) {
                    visitor.visit_i64(x)
                } else if let Ok(x) = i128::try_from(*x) {
                    visitor.visit_i128(x)
                } else {
                    unreachable!()
                }
            }

            Value::Float(x) => visitor.visit_f64(*x),
        }
    }

    #[inline]
    fn deserialize_bool<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        let mut value = self.0;
        while let Value::Tag(.., v) = value {
            value = v;
        }

        match value {
            Value::Bool(x) => visitor.visit_bool(*x),
            _ => Err(de::Error::invalid_type(value.into(), &"bool")),
        }
    }

    #[inline]
    fn deserialize_f32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        self.deserialize_f64(visitor)
    }

    #[inline]
    fn deserialize_f64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        let mut value = self.0;
        while let Value::Tag(.., v) = value {
            value = v;
        }

        match value {
            Value::Float(x) => visitor.visit_f64(*x),
            _ => Err(de::Error::invalid_type(value.into(), &"f64")),
        }
    }

    fn deserialize_i8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_i8(self.integer("i8")?)
    }

    fn deserialize_i16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_i16(self.integer("i16")?)
    }

    fn deserialize_i32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_i32(self.integer("i32")?)
    }

    fn deserialize_i64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_i64(self.integer("i64")?)
    }

    fn deserialize_i128<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_i128(self.integer("i128")?)
    }

    fn deserialize_u8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_u8(self.integer("u8")?)
    }

    fn deserialize_u16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_u16(self.integer("u16")?)
    }

    fn deserialize_u32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_u32(self.integer("u32")?)
    }

    fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_u64(self.integer("u64")?)
    }

    fn deserialize_u128<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        visitor.visit_u128(self.integer("u128")?)
    }

    fn deserialize_char<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        let mut value = self.0;
        while let Value::Tag(.., v) = value {
            value = v;
        }

        match value {
            Value::Text(x) => match x.chars().count() {
                1 => visitor.visit_char(x.chars().next().unwrap()),
                _ => Err(de::Error::invalid_type(value.into(), &"char")),
            },

            _ => Err(de::Error::invalid_type(value.into(), &"char")),
        }
    }

    fn deserialize_str<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        let mut value = self.0;
        while let Value::Tag(.., v) = value {
            value = v;
        }

        match value {
            Value::Text(x) => visitor.visit_str(x),
            _ => Err(de::Error::invalid_type(value.into(), &"str")),
        }
    }

    fn deserialize_string<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        self.deserialize_str(visitor)
    }

    fn deserialize_bytes<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        let mut value = self.0;
        while let Value::Tag(.., v) = value {
            value = v;
        }

        match value {
            Value::Bytes(x) => visitor.visit_bytes(x),
            _ => Err(de::Error::invalid_type(value.into(), &"bytes")),
        }
    }

    fn deserialize_byte_buf<V: de::Visitor<'de>>(
        self,
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_bytes(visitor)
    }

    fn deserialize_seq<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        let mut value = self.0;
        while let Value::Tag(.., v) = value {
            value = v;
        }

        match value {
            Value::Array(x) => visitor.visit_seq(Deserializer(x.iter())),
            _ => Err(de::Error::invalid_type(value.into(), &"array")),
        }
    }

    fn deserialize_map<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        let mut value = self.0;
        while let Value::Tag(.., v) = value {
            value = v;
        }

        match value {
            Value::Map(x) => visitor.visit_map(Deserializer(x.iter().peekable())),
            _ => Err(de::Error::invalid_type(value.into(), &"map")),
        }
    }

    fn deserialize_struct<V: de::Visitor<'de>>(
        self,
        _name: &'static str,
        _fields: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_map(visitor)
    }

    fn deserialize_tuple<V: de::Visitor<'de>>(
        self,
        _len: usize,
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_seq(visitor)
    }

    fn deserialize_tuple_struct<V: de::Visitor<'de>>(
        self,
        _name: &'static str,
        _len: usize,
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_seq(visitor)
    }

    fn deserialize_identifier<V: de::Visitor<'de>>(
        self,
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_str(visitor)
    }

    fn deserialize_ignored_any<V: de::Visitor<'de>>(
        self,
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_any(visitor)
    }

    #[inline]
    fn deserialize_option<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            Value::Null => visitor.visit_none(),
            x => visitor.visit_some(Self(x)),
        }
    }

    #[inline]
    fn deserialize_unit<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
        match self.0 {
            Value::Null => visitor.visit_unit(),
            _ => Err(de::Error::invalid_type(self.0.into(), &"null")),
        }
    }

    #[inline]
    fn deserialize_unit_struct<V: de::Visitor<'de>>(
        self,
        _name: &'static str,
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_unit(visitor)
    }

    #[inline]
    fn deserialize_newtype_struct<V: de::Visitor<'de>>(
        self,
        _name: &'static str,
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        visitor.visit_newtype_struct(self)
    }

    #[inline]
    fn deserialize_enum<V: de::Visitor<'de>>(
        self,
        name: &'static str,
        variants: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        if name == "@@TAG@@" {
            let (tag, val) = match self.0 {
                Value::Tag(t, v) => (Some(*t), v.as_ref()),
                v => (None, v),
            };

            let parent: Deserializer<&Value> = Deserializer(val);
            let access = crate::tag::TagAccess::new(parent, tag);
            return visitor.visit_enum(access);
        }

        match self.0 {
            Value::Tag(.., v) => Deserializer(v.as_ref()).deserialize_enum(name, variants, visitor),
            Value::Map(x) if x.len() == 1 => visitor.visit_enum(Deserializer(&x[0])),
            x @ Value::Text(..) => visitor.visit_enum(Deserializer(x)),
            _ => Err(de::Error::invalid_type(self.0.into(), &"map")),
        }
    }
}

impl<'a, 'de, T: Iterator<Item = &'a Value>> de::SeqAccess<'de> for Deserializer<T> {
    type Error = Error;

    #[inline]
    fn next_element_seed<U: de::DeserializeSeed<'de>>(
        &mut self,
        seed: U,
    ) -> Result<Option<U::Value>, Self::Error> {
        match self.0.next() {
            None => Ok(None),
            Some(v) => seed.deserialize(Deserializer(v)).map(Some),
        }
    }
}

impl<'a, 'de, T: Iterator<Item = &'a (Value, Value)>> de::MapAccess<'de>
    for Deserializer<Peekable<T>>
{
    type Error = Error;

    #[inline]
    fn next_key_seed<K: de::DeserializeSeed<'de>>(
        &mut self,
        seed: K,
    ) -> Result<Option<K::Value>, Self::Error> {
        match self.0.peek() {
            None => Ok(None),
            Some(x) => Ok(Some(seed.deserialize(Deserializer(&x.0))?)),
        }
    }

    #[inline]
    fn next_value_seed<V: de::DeserializeSeed<'de>>(
        &mut self,
        seed: V,
    ) -> Result<V::Value, Self::Error> {
        seed.deserialize(Deserializer(&self.0.next().unwrap().1))
    }
}

impl<'a, 'de> de::EnumAccess<'de> for Deserializer<&'a (Value, Value)> {
    type Error = Error;
    type Variant = Deserializer<&'a Value>;

    #[inline]
    fn variant_seed<V: de::DeserializeSeed<'de>>(
        self,
        seed: V,
    ) -> Result<(V::Value, Self::Variant), Self::Error> {
        let k = seed.deserialize(Deserializer(&self.0 .0))?;
        Ok((k, Deserializer(&self.0 .1)))
    }
}

impl<'a, 'de> de::EnumAccess<'de> for Deserializer<&'a Value> {
    type Error = Error;
    type Variant = Deserializer<&'a Value>;

    #[inline]
    fn variant_seed<V: de::DeserializeSeed<'de>>(
        self,
        seed: V,
    ) -> Result<(V::Value, Self::Variant), Self::Error> {
        let k = seed.deserialize(self)?;
        Ok((k, Deserializer(&Value::Null)))
    }
}

impl<'a, 'de> de::VariantAccess<'de> for Deserializer<&'a Value> {
    type Error = Error;

    #[inline]
    fn unit_variant(self) -> Result<(), Self::Error> {
        match self.0 {
            Value::Null => Ok(()),
            _ => Err(de::Error::invalid_type(self.0.into(), &"unit")),
        }
    }

    #[inline]
    fn newtype_variant_seed<U: de::DeserializeSeed<'de>>(
        self,
        seed: U,
    ) -> Result<U::Value, Self::Error> {
        seed.deserialize(self)
    }

    #[inline]
    fn tuple_variant<V: de::Visitor<'de>>(
        self,
        _len: usize,
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_seq(visitor)
    }

    #[inline]
    fn struct_variant<V: de::Visitor<'de>>(
        self,
        _fields: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value, Self::Error> {
        self.deserialize_map(visitor)
    }
}

impl Value {
    /// Deserializes the `Value` into an object
    #[inline]
    pub fn deserialized<'de, T: de::Deserialize<'de>>(&self) -> Result<T, Error> {
        T::deserialize(Deserializer(self))
    }
}
