use super::Node;
use serde::de::{Error, MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;

const FIELDS: [&str; 3] = ["type", "attributes", "children"];

impl<T: Serialize> Serialize for Node<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut map = serializer.serialize_map(Some(3))?;
        map.serialize_entry("type", &self.tag)?;
        if !self.attributes.is_empty() {
            map.serialize_entry("attributes", &self.attributes)?;
        }
        if !self.children.is_empty() {
            map.serialize_entry("children", &self.children)?;
        }
        map.end()
    }
}

struct NodeVisitor<T> {
    result: Node<T>,
}

impl<T> Default for NodeVisitor<T> {
    fn default() -> Self {
        Self {
            result: Node::<T>::from(String::default()),
        }
    }
}

impl<'de, T: Deserialize<'de>> Visitor<'de> for NodeVisitor<T> {
    type Value = Node<T>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("an map with properties type, name, attributes and children")
    }

    fn visit_map<M>(mut self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        while let Some(key) = access.next_key::<String>()? {
            if key == "type" {
                self.result.tag = access.next_value()?;
            } else if key == "attributes" {
                self.result.attributes = access.next_value()?;
            } else if key == "children" {
                self.result.children = access.next_value()?;
            } else {
                return Err(M::Error::unknown_field(&key, &FIELDS));
            }
        }
        if self.result.tag.is_empty() {
            return Err(M::Error::missing_field("type"));
        }
        Ok(self.result)
    }
}

impl<'de, T: Deserialize<'de>> Deserialize<'de> for Node<T> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(NodeVisitor::<T>::default())
    }
}

#[cfg(test)]
mod tests {
    use crate::node::Node;
    use crate::text::Text;

    #[test]
    fn serialize() {
        let mut elt = Node::<Text>::from("span");
        elt.attributes.insert("margin-bottom".into(), "20px".into());
        elt.children.push(Text::from("Hello"));
        assert_eq!(
            serde_json::to_string(&elt).unwrap(),
            r#"{"type":"span","attributes":{"margin-bottom":"20px"},"children":["Hello"]}"#
        );
    }

    #[test]
    fn deserialize() {
        let json = r#"{"type":"span","attributes":{"margin-bottom":"20px"},"children":["Hello","World","!"]}"#;
        let res: Node<Text> = serde_json::from_str(json).unwrap();
        assert_eq!(res.attributes.len(), 1);
        assert_eq!(res.children.len(), 3);
    }
}
