use std::{
    collections::HashMap,
    fmt::{Display, Formatter},
    str::FromStr,
};

use serde::{Deserialize as _, Deserializer, Serialize as _, Serializer};

use crate::{
    EdgeId, NodeId,
    edge::Edge,
    id::EmptyId,
    node::{GenericNodeInfo, Node},
};

/// Errors that can occur when loading/saving or modifing a [`Canvas`].
#[derive(Debug, thiserror::Error)]
pub enum CanvasError {
    /// A [`Node`] with that ID already exists in the [`Canvas`].
    #[error("Node {0} already exists")]
    NodeExists(NodeId),
    /// A [`Edge`] with that ID already exists in the [`Canvas`].
    #[error("Edge {0} already exists")]
    EdgeExists(EdgeId),
    /// A [`Node`] does not exists with that ID and the [`Edge`] is unable to connect to it as a result.
    #[error("Node {0} does not exist")]
    NodeNotExists(NodeId),
    /// Failed to parse the raw JSON to the [`Canvas`] struct.
    #[error(transparent)]
    ParseError(#[from] serde_json::Error),
    /// The [`Node`] or [`Edge`] ID supplied is empty.
    ///
    /// This isn't *not* allowed by the spec, but there should never be a reason where this is the case.
    #[error(transparent)]
    EmptyId(#[from] EmptyId),
}

/// Main struct for the canvas
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Canvas {
    #[serde(
        serialize_with = "serialize_as_vec_node",
        deserialize_with = "deserialize_as_map_node"
    )]
    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
    nodes: HashMap<NodeId, Node>,
    #[serde(
        serialize_with = "serialize_as_vec_edge",
        deserialize_with = "deserialize_as_map_edge"
    )]
    #[serde(skip_serializing_if = "HashMap::is_empty", default)]
    edges: HashMap<EdgeId, Edge>,
}
fn serialize_as_vec_node<S>(data: &HashMap<NodeId, Node>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let vec: Vec<&Node> = data.values().collect();
    vec.serialize(serializer)
}

fn deserialize_as_map_node<'de, D>(deserializer: D) -> Result<HashMap<NodeId, Node>, D::Error>
where
    D: Deserializer<'de>,
{
    let vec: Vec<Node> = Vec::deserialize(deserializer)?;
    let map: HashMap<_, _> = vec
        .into_iter()
        .map(|node| (node.id().clone(), node))
        .collect();
    Ok(map)
}

fn serialize_as_vec_edge<S>(data: &HashMap<EdgeId, Edge>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let vec: Vec<&Edge> = data.values().collect();
    vec.serialize(serializer)
}

fn deserialize_as_map_edge<'de, D>(deserializer: D) -> Result<HashMap<EdgeId, Edge>, D::Error>
where
    D: Deserializer<'de>,
{
    let vec: Vec<Edge> = Vec::deserialize(deserializer)?;
    let map: HashMap<_, _> = vec
        .into_iter()
        .map(|node| (node.id().clone(), node))
        .collect();
    Ok(map)
}

impl Canvas {
    /// Add a node to the canvas.
    ///
    /// # Errors
    ///
    /// This is fail if the node already exists in the canvas.
    pub fn add_node(&mut self, node: Node) -> Result<(), CanvasError> {
        if self.nodes.contains_key(node.id()) {
            return Err(CanvasError::NodeExists(node.id().clone()));
        }
        self.nodes.insert(node.id().clone(), node);
        Ok(())
    }

    /// Add a edge to the canvas.
    ///
    /// # Errors
    ///
    /// This is fail if the edge already exists in the canvas,
    /// or the start and end nodes don't exist.
    pub fn add_edge(&mut self, edge: Edge) -> Result<(), CanvasError> {
        if self.edges.contains_key(edge.id()) {
            return Err(CanvasError::EdgeExists(edge.id().clone()));
        }

        if !self.nodes.contains_key(edge.from_node()) {
            return Err(CanvasError::NodeNotExists(edge.from_node().clone()));
        }

        if !self.nodes.contains_key(edge.to_node()) {
            return Err(CanvasError::NodeNotExists(edge.to_node().clone()));
        }

        self.edges.insert(edge.id().clone(), edge);
        Ok(())
    }

    /// Returns a mutable reference of a specific [`Node`] from this canvas.
    pub fn get_node(&mut self, id: &NodeId) -> Option<&mut Node> {
        self.nodes.get_mut(id)
    }

    /// Returns a mutable reference of a specific [`Edge`] from this canvas.
    pub fn get_edge(&mut self, id: &EdgeId) -> Option<&mut Edge> {
        self.edges.get_mut(id)
    }

    /// Get a reference to all the [`Node`]s of this canvas.
    #[must_use]
    pub const fn get_nodes(&self) -> &HashMap<NodeId, Node> {
        &self.nodes
    }

    /// Get a mutable reference to all the [`Node`]s of this canvas.
    pub const fn get_mut_nodes(&mut self) -> &mut HashMap<NodeId, Node> {
        &mut self.nodes
    }

    /// Get a reference to all the [`Edge`]s of this canvas.
    #[must_use]
    pub const fn get_edges(&self) -> &HashMap<EdgeId, Edge> {
        &self.edges
    }

    /// Get a mutable reference to all the [`Edge`]s of this canvas.
    pub const fn get_mut_edges(&mut self) -> &mut HashMap<EdgeId, Edge> {
        &mut self.edges
    }
}

impl FromStr for Canvas {
    type Err = CanvasError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(serde_json::from_str(s)?)
    }
}

impl Display for Canvas {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", serde_json::to_string(self).unwrap())
    }
}
