use std::{borrow::Cow, marker::PhantomData};

use serde::de::{Error, MapAccess, Visitor};

use super::{
    content::{Content, ContentDeserializer},
    cow_str::CowStr,
    key_de::KeyDeserializer,
};

pub struct InlineFragmentVisitor<T> {
    phantom: PhantomData<fn() -> T>,
}

impl<T> InlineFragmentVisitor<T> {
    #[allow(clippy::new_without_default)]
    pub fn new() -> Self {
        InlineFragmentVisitor {
            phantom: PhantomData,
        }
    }
}

impl<'de, T> Visitor<'de> for InlineFragmentVisitor<T>
where
    T: crate::core::InlineFragments<'de>,
{
    type Value = T;

    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter.write_str("a map")
    }

    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut buffer = Vec::new();

        while let Some(key) = access.next_key::<CowStr<'_>>()? {
            let key = key.into_inner();
            if key == "__typename" {
                let typename = access.next_value::<CowStr<'_>>()?.into_inner();

                // Put __typename into the buffer incase it's needed by the inner decoder
                buffer.push((
                    Cow::Borrowed("__typename"),
                    Content::String(typename.as_ref().to_string()),
                ));

                return T::deserialize_variant(
                    typename.as_ref(),
                    BufferDeserializer { access, buffer },
                );
            }
            buffer.push((key, access.next_value::<Content<'_>>()?))
        }

        Err(M::Error::missing_field("__typename"))
    }
}

struct BufferDeserializer<'de, M: MapAccess<'de>> {
    access: M,
    buffer: Vec<(Cow<'de, str>, Content<'de>)>,
}

impl<'de, M> serde::de::Deserializer<'de> for BufferDeserializer<'de, M>
where
    M: MapAccess<'de>,
{
    type Error = M::Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        visitor.visit_map(BufferMapAccess {
            access: self.access,
            buffer: self.buffer,
            next_content: None,
        })
    }

    serde::forward_to_deserialize_any! {
        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
        bytes byte_buf option unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }
}

struct BufferMapAccess<'de, M: MapAccess<'de>> {
    access: M,
    buffer: Vec<(Cow<'de, str>, Content<'de>)>,
    next_content: Option<Content<'de>>,
}

impl<'de, M> serde::de::MapAccess<'de> for BufferMapAccess<'de, M>
where
    M: MapAccess<'de>,
{
    type Error = M::Error;

    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
    where
        K: serde::de::DeserializeSeed<'de>,
    {
        if let Some((key, content)) = self.buffer.pop() {
            self.next_content = Some(content);
            return seed.deserialize(KeyDeserializer::new(key)).map(Some);
        }

        self.access.next_key_seed(seed)
    }

    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
    where
        V: serde::de::DeserializeSeed<'de>,
    {
        if let Some(content) = self.next_content.take() {
            return seed.deserialize(ContentDeserializer::new(content));
        }

        self.access.next_value_seed(seed)
    }
}
