use std::mem::transmute;
use libquickjs_ng_sys::JSContext;
use serde::de::{
    self, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, VariantAccess,
    Visitor,
};
use serde::{forward_to_deserialize_any, Deserialize};
use crate::utils::deserialize_borrowed_str;
use crate::value::{JsTag, OwnedJsArray, OwnedJsObject, OwnedJsPropertyIterator, OwnedJsValue};
use super::error::{Error, Result};
pub struct Deserializer<'de> {
    context: *mut JSContext,
    root: &'de OwnedJsValue,
    paths: Vec<(OwnedJsValue, u32, Option<OwnedJsPropertyIterator>)>,
    current: Option<OwnedJsValue>,
}
impl<'de> Deserializer<'de> {
    fn from_js(context: *mut JSContext, root: &'de OwnedJsValue) -> Self {
        Deserializer {
            context,
            root,
            paths: Vec::new(),
            current: Some(root.clone()),
        }
    }
}
pub fn from_js<'a, T>(context: *mut JSContext, value: &'a OwnedJsValue) -> Result<T>
where
    T: Deserialize<'a>,
{
    let mut deserializer = Deserializer::from_js(context, value);
    let t = T::deserialize(&mut deserializer)?;
    Ok(t)
}
impl<'de> Deserializer<'de> {
    fn get_current(&self) -> &OwnedJsValue {
        if let Some(current) = self.current.as_ref() {
            current
        } else {
            self.root
        }
    }
    fn next(&mut self) -> Result<Option<()>> {
        let (current, index, obj_iter) = self.paths.last_mut().expect("current must be Some");
        let next = if current.is_array() {
            let current = OwnedJsArray::try_from_value(current.clone()).unwrap();
            let item = current.get_index(*index)?;
            if item.is_some() {
                self.current = item;
                *index += 1;
                Some(())
            } else {
                None
            }
        } else if current.is_object() {
            let obj_iter = obj_iter.as_mut().expect("obj_iter must be Some");
            if let Some(ret) = obj_iter.next() {
                self.current = Some(ret?);
                *index += 1;
                Some(())
            } else {
                None
            }
        } else {
            return Err(Error::ExpectedArrayOrObject);
        };
        if next.is_some() {
            Ok(next)
        } else {
            Ok(None)
        }
    }
    fn guard_circular_reference(&self, current: &OwnedJsValue) -> Result<()> {
        if let Some(_) = self.paths.iter().find(|(p, _, _)| p == current) {
            Err(Error::CircularReference)
        } else {
            Ok(())
        }
    }
    fn enter_array(&mut self) -> Result<()> {
        let current = self.get_current().clone();
        if current.is_array() {
            self.guard_circular_reference(¤t)?;
            self.paths.push((current, 0, None));
            Ok(())
        } else {
            Err(Error::ExpectedArray)
        }
    }
    fn enter_object(&mut self) -> Result<()> {
        let current = self.get_current().clone();
        if current.is_object() {
            let obj = OwnedJsObject::try_from_value(current.clone()).unwrap();
            self.guard_circular_reference(¤t)?;
            self.paths.push((current, 0, Some(obj.properties_iter()?)));
            Ok(())
        } else {
            Err(Error::ExpectedObject)
        }
    }
    fn leave(&mut self) {
        if let Some((current, _, _)) = self.paths.pop() {
            self.current = Some(current);
        }
    }
    fn parse_string(&mut self) -> Result<String> {
        let current = self.get_current();
        if current.is_string() {
            current.to_string().map_err(|err| err.into())
        } else {
            Err(Error::ExpectedString)
        }
    }
    fn parse_borrowed_str(&mut self) -> Result<&'de str> {
        let current = self.get_current();
        if current.is_string() {
            let s = deserialize_borrowed_str(self.context, ¤t.value).unwrap();
            let s = unsafe { transmute(s) };
            Ok(s)
        } else {
            Err(Error::ExpectedString)
        }
    }
}
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
    type Error = Error;
    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        let current = self.get_current();
        match current.tag() {
            JsTag::Undefined => visitor.visit_unit(),
            JsTag::Int => visitor.visit_i32(current.to_int()?),
            JsTag::Bool => visitor.visit_bool(current.to_bool()?),
            JsTag::Null => visitor.visit_unit(),
            JsTag::String => visitor.visit_string(current.to_string()?),
            JsTag::Float64 => visitor.visit_f64(current.to_float()?),
            JsTag::Object => {
                if current.is_array() {
                    self.deserialize_seq(visitor)
                } else {
                    self.deserialize_map(visitor)
                }
            }
            JsTag::Symbol => visitor.visit_unit(),
            JsTag::Module => visitor.visit_unit(),
            JsTag::Exception => self.deserialize_map(visitor),
            JsTag::CatchOffset => visitor.visit_unit(),
            JsTag::Uninitialized => visitor.visit_unit(),
            JsTag::FunctionBytecode => visitor.visit_unit(),
            #[cfg(feature = "bigint")]
            JsTag::BigInt => {
                let bigint = current.to_bigint()?;
                visitor.visit_i64(bigint.as_i64().ok_or(Error::BigIntOverflow)?)
            } }
    }
    forward_to_deserialize_any! {
        bool
        i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64
        string char
        unit
        identifier ignored_any
    }
    fn deserialize_str<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        visitor.visit_borrowed_str(self.parse_borrowed_str()?)
    }
    fn deserialize_seq<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.enter_array()?;
        let r = visitor.visit_seq(&mut *self);
        self.leave();
        r
    }
    fn deserialize_bytes<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.deserialize_byte_buf(visitor)
    }
    fn deserialize_byte_buf<V>(self, _: V) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        unimplemented!("borrowed bytes not supported yet")
        }
    fn deserialize_tuple<V>(
        self,
        _len: usize,
        visitor: V,
    ) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.deserialize_seq(visitor)
    }
    fn deserialize_tuple_struct<V>(
        self,
        _name: &'static str,
        _len: usize,
        visitor: V,
    ) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.deserialize_seq(visitor)
    }
    fn deserialize_option<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        if self.get_current().is_null() || self.get_current().is_undefined() {
            visitor.visit_none()
        } else {
            visitor.visit_some(self)
        }
    }
    fn deserialize_newtype_struct<V>(
        self,
        _name: &'static str,
        visitor: V,
    ) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        visitor.visit_newtype_struct(self)
    }
    fn deserialize_map<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.enter_object()?;
        let r = visitor.visit_map(&mut *self);
        self.leave();
        r
    }
    fn deserialize_struct<V>(
        self,
        _name: &'static str,
        _fields: &'static [&'static str],
        visitor: V,
    ) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.deserialize_map(visitor)
    }
    fn deserialize_enum<V>(
        self,
        _name: &'static str,
        _variants: &'static [&'static str],
        visitor: V,
    ) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        if self.get_current().is_object() {
            self.enter_object()?;
            self.next()?;
            let r = visitor.visit_enum(Enum::new(self));
            self.leave();
            r
        } else {
            visitor.visit_enum(self.parse_string()?.into_deserializer())
        }
    }
    fn deserialize_unit_struct<V>(
        self,
        _name: &'static str,
        visitor: V,
    ) -> std::result::Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.deserialize_unit(visitor)
    }
}
impl<'de, 'a> SeqAccess<'de> for Deserializer<'de> {
    type Error = Error;
    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
    where
        T: DeserializeSeed<'de>,
    {
        if let Some(_) = self.next()? {
            seed.deserialize(self).map(Some)
        } else {
            Ok(None)
        }
    }
}
impl<'de, 'a> MapAccess<'de> for Deserializer<'de> {
    type Error = Error;
    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
    where
        K: DeserializeSeed<'de>,
    {
        if let Some(_) = self.next()? {
            seed.deserialize(self).map(Some)
        } else {
            Ok(None)
        }
    }
    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
    where
        V: DeserializeSeed<'de>,
    {
        self.next()?;
        seed.deserialize(self)
    }
}
struct Enum<'a, 'de: 'a> {
    de: &'a mut Deserializer<'de>,
}
impl<'a, 'de> Enum<'a, 'de> {
    fn new(de: &'a mut Deserializer<'de>) -> Self {
        Enum { de }
    }
}
impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
    type Error = Error;
    type Variant = Self;
    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
    where
        V: DeserializeSeed<'de>,
    {
        let val = seed.deserialize(&mut *self.de)?;
        self.de.next()?;
        Ok((val, self))
        }
}
impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> {
    type Error = Error;
    fn unit_variant(self) -> Result<()> {
        Err(Error::ExpectedString)
    }
    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
    where
        T: DeserializeSeed<'de>,
    {
        seed.deserialize(self.de)
    }
    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        de::Deserializer::deserialize_seq(self.de, visitor)
    }
    fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        de::Deserializer::deserialize_map(self.de, visitor)
    }
}