use std::collections::{BTreeMap, HashMap};

use crate::PortDirection;

/// Unique identifier for a node instance within a network.
pub type NodeId = u64;

/// A named, directed port on a node.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Port {
    pub name: String,
    pub direction: PortDirection,
}

/// A reference to a specific port on a specific node instance.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PortRef {
    pub node_id: NodeId,
    pub port_name: String,
}

/// A directed connection between two ports.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Connection {
    pub from: PortRef,
    pub to: PortRef,
}

/// A node in the network. Leaf nodes represent external components;
/// nodes with non-empty `children` represent groups (composites).
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Node {
    pub id: NodeId,
    pub name: String,
    /// Schema name for leaf nodes (e.g. `"Source"`); `"group"` for composites.
    pub type_name: String,
    pub ports: Vec<Port>,
    pub children: Vec<Node>,
    /// Resolved node attribute values. Always empty for groups and the
    /// synthetic top-level array container; attributes are nodes-only.
    /// `#[serde(default)]` keeps deserialization of JSON emitted before this
    /// field existed non-breaking.
    #[cfg_attr(feature = "serde", serde(default))]
    pub attributes: BTreeMap<String, String>,
}

/// The complete output of a Nexus program.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Network {
    /// `None` for programs that produce no network.
    pub root: Option<Node>,
    /// All connections in the network, globally owned.
    pub connections: Vec<Connection>,
}

/// One step of a depth-first walk over a `Node` tree (see [`Network::walk`]).
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum WalkEvent<'a> {
    /// A composite node (non-empty `children`), emitted before its children.
    Enter(&'a Node),
    /// A node with no children.
    Leaf(&'a Node),
    /// The matching close for `Enter`, emitted after the last child.
    Leave(&'a Node),
}

/// Explicit-stack depth-first iterator driving [`Network::walk`].
///
/// Each stack frame is a node paired with an iterator over its remaining
/// children; `Enter` is emitted when a frame is pushed and `Leave` when its
/// child iterator is exhausted and the frame is popped.
struct Walk<'a> {
    stack: Vec<(&'a Node, std::slice::Iter<'a, Node>)>,
    root: Option<&'a Node>,
}

impl<'a> Iterator for Walk<'a> {
    type Item = WalkEvent<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(root) = self.root.take() {
            return Some(if root.children.is_empty() {
                WalkEvent::Leaf(root)
            } else {
                self.stack.push((root, root.children.iter()));
                WalkEvent::Enter(root)
            });
        }

        let (_, children) = self.stack.last_mut()?;
        match children.next() {
            Some(child) if child.children.is_empty() => Some(WalkEvent::Leaf(child)),
            Some(child) => {
                let iter = child.children.iter();
                self.stack.push((child, iter));
                Some(WalkEvent::Enter(child))
            }
            None => {
                let (node, _) = self.stack.pop().unwrap();
                Some(WalkEvent::Leave(node))
            }
        }
    }
}

impl Network {
    /// Depth-first walk of the node tree; yields nothing when `root` is `None`.
    pub fn walk(&self) -> impl Iterator<Item = WalkEvent<'_>> {
        Walk {
            stack: Vec::new(),
            root: self.root.as_ref(),
        }
    }

    /// Build a map from `NodeId` to fully qualified path (e.g. `"pipeline/source"`).
    ///
    /// Returns an empty map when `root` is `None`.
    #[must_use]
    pub fn node_paths(&self) -> HashMap<NodeId, String> {
        let mut map = HashMap::new();
        let mut segments: Vec<&str> = Vec::new();
        for event in self.walk() {
            match event {
                WalkEvent::Enter(node) => {
                    segments.push(&node.name);
                    map.insert(node.id, segments.join("/"));
                }
                WalkEvent::Leaf(node) => {
                    segments.push(&node.name);
                    map.insert(node.id, segments.join("/"));
                    segments.pop();
                }
                WalkEvent::Leave(_) => {
                    segments.pop();
                }
            }
        }
        map
    }

    /// Build a flat map from `NodeId` to `&Node` for O(1) lookup.
    ///
    /// Returns an empty map when `root` is `None`.
    #[must_use]
    pub fn node_index(&self) -> HashMap<NodeId, &Node> {
        let mut map = HashMap::new();
        for event in self.walk() {
            match event {
                WalkEvent::Enter(node) | WalkEvent::Leaf(node) => {
                    map.insert(node.id, node);
                }
                WalkEvent::Leave(_) => {}
            }
        }
        map
    }

    /// Render this network as a Graphviz DOT digraph (`README_INTEGRATION.md` Layer 4).
    /// A pure formatter: no interpreter involvement, and safe to call on a hand-built
    /// `Network` as well as one produced by [`crate::Nexus::run`].
    #[must_use]
    pub fn to_dot(&self) -> String {
        crate::format::dot::render(self)
    }

    /// Render this network as a Mermaid `flowchart` diagram (`README_INTEGRATION.md` Layer 4b).
    /// A pure formatter: no interpreter involvement, and safe to call on a hand-built
    /// `Network` as well as one produced by [`crate::Nexus::run`].
    #[must_use]
    pub fn to_mermaid(&self) -> String {
        crate::format::mermaid::render(self)
    }

    /// Render this network as a `PlantUML` component diagram (`README_INTEGRATION.md` Layer 4c).
    /// A pure formatter: no interpreter involvement, and safe to call on a hand-built
    /// `Network` as well as one produced by [`crate::Nexus::run`].
    #[must_use]
    pub fn to_plantuml(&self) -> String {
        crate::format::plantuml::render(self)
    }
}

#[cfg(all(test, feature = "serde"))]
mod serde_tests {
    use super::*;

    /// The `serde` feature's whole job is making the `Network` data model (de)serializable
    /// (it powers the CLI's `--output json`). Pin that it round-trips through JSON, attributes
    /// and nested children included, so the feature-gated derives can't silently rot.
    #[test]
    fn network_json_round_trip() {
        let network = Network {
            root: Some(Node {
                id: 0,
                name: "root".into(),
                type_name: "group".into(),
                ports: vec![Port {
                    name: "out".into(),
                    direction: PortDirection::Out,
                }],
                children: vec![Node {
                    id: 1,
                    name: "child".into(),
                    type_name: "Source".into(),
                    ports: vec![],
                    children: vec![],
                    attributes: BTreeMap::from([("rate".into(), "44100".into())]),
                }],
                attributes: BTreeMap::new(),
            }),
            connections: vec![Connection {
                from: PortRef {
                    node_id: 1,
                    port_name: "out".into(),
                },
                to: PortRef {
                    node_id: 0,
                    port_name: "out".into(),
                },
            }],
        };

        let json = serde_json::to_string(&network).expect("Network should serialize");
        let restored: Network = serde_json::from_str(&json).expect("Network should deserialize");

        assert_eq!(network, restored);
    }
}
