//! Graph node (vertex) definition.

use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::sync::atomic::{AtomicU64, Ordering};

/// Unique node identifier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct NodeId(pub [u8; 32]);

impl NodeId {
    /// Creates a new unique node ID.
    pub fn new() -> Self {
        static COUNTER: AtomicU64 = AtomicU64::new(1);
        let id = COUNTER.fetch_add(1, Ordering::SeqCst);
        let mut bytes = [0u8; 32];
        bytes[..8].copy_from_slice(&id.to_be_bytes());
        // Add timestamp for uniqueness
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos() as u64;
        bytes[8..16].copy_from_slice(&now.to_be_bytes());
        NodeId(*blake3::hash(&bytes).as_bytes())
    }

    /// Creates from raw bytes.
    pub fn from_bytes(bytes: [u8; 32]) -> Self {
        NodeId(bytes)
    }

    /// Creates from hex string.
    pub fn from_hex(hex: &str) -> Option<Self> {
        let bytes = hex::decode(hex).ok()?;
        if bytes.len() != 32 {
            return None;
        }
        let mut arr = [0u8; 32];
        arr.copy_from_slice(&bytes);
        Some(NodeId(arr))
    }

    /// Returns the bytes.
    pub fn as_bytes(&self) -> &[u8; 32] {
        &self.0
    }

    /// Converts to hex string.
    pub fn to_hex(&self) -> String {
        hex::encode(self.0)
    }
}

impl Default for NodeId {
    fn default() -> Self {
        Self::new()
    }
}

impl std::fmt::Display for NodeId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "node_{}", hex::encode(&self.0[..8]))
    }
}

/// A node (vertex) in the graph.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Node {
    /// Unique node ID.
    pub id: NodeId,
    /// Labels (types) for this node (e.g., "User", "Product").
    pub labels: Vec<String>,
    /// Properties stored as JSON.
    pub properties: JsonValue,
    /// Creation timestamp.
    pub created_at: u64,
    /// Last update timestamp.
    pub updated_at: u64,
}

impl Node {
    /// Creates a new node with the given labels and properties.
    pub fn new(labels: Vec<String>, properties: JsonValue) -> Self {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64;

        Self {
            id: NodeId::new(),
            labels,
            properties,
            created_at: now,
            updated_at: now,
        }
    }

    /// Creates a node with a specific ID.
    pub fn with_id(id: NodeId, labels: Vec<String>, properties: JsonValue) -> Self {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64;

        Self {
            id,
            labels,
            properties,
            created_at: now,
            updated_at: now,
        }
    }

    /// Returns true if this node has the given label.
    pub fn has_label(&self, label: &str) -> bool {
        self.labels.iter().any(|l| l == label)
    }

    /// Adds a label to this node.
    pub fn add_label(&mut self, label: impl Into<String>) {
        let label = label.into();
        if !self.labels.contains(&label) {
            self.labels.push(label);
            self.touch();
        }
    }

    /// Removes a label from this node.
    pub fn remove_label(&mut self, label: &str) -> bool {
        if let Some(pos) = self.labels.iter().position(|l| l == label) {
            self.labels.remove(pos);
            self.touch();
            true
        } else {
            false
        }
    }

    /// Gets a property value.
    pub fn get_property(&self, key: &str) -> Option<&JsonValue> {
        self.properties.get(key)
    }

    /// Sets a property value.
    pub fn set_property(&mut self, key: &str, value: JsonValue) {
        if let Some(obj) = self.properties.as_object_mut() {
            obj.insert(key.to_string(), value);
            self.touch();
        }
    }

    /// Removes a property.
    pub fn remove_property(&mut self, key: &str) -> Option<JsonValue> {
        if let Some(obj) = self.properties.as_object_mut() {
            let removed = obj.remove(key);
            if removed.is_some() {
                self.touch();
            }
            removed
        } else {
            None
        }
    }

    /// Updates the updated_at timestamp.
    fn touch(&mut self) {
        self.updated_at = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64;
    }

    /// Checks if the node matches a property filter.
    pub fn matches_properties(&self, filter: &JsonValue) -> bool {
        if let (Some(filter_obj), Some(props_obj)) = (filter.as_object(), self.properties.as_object()) {
            for (key, expected) in filter_obj {
                if let Some(actual) = props_obj.get(key) {
                    if actual != expected {
                        return false;
                    }
                } else {
                    return false;
                }
            }
            true
        } else {
            filter == &self.properties
        }
    }
}

/// Builder for creating nodes.
pub struct NodeBuilder {
    labels: Vec<String>,
    properties: serde_json::Map<String, JsonValue>,
}

impl NodeBuilder {
    /// Creates a new node builder.
    pub fn new() -> Self {
        Self {
            labels: Vec::new(),
            properties: serde_json::Map::new(),
        }
    }

    /// Adds a label.
    pub fn label(mut self, label: impl Into<String>) -> Self {
        self.labels.push(label.into());
        self
    }

    /// Adds multiple labels.
    pub fn labels(mut self, labels: Vec<String>) -> Self {
        self.labels.extend(labels);
        self
    }

    /// Sets a property.
    pub fn property(mut self, key: impl Into<String>, value: impl Into<JsonValue>) -> Self {
        self.properties.insert(key.into(), value.into());
        self
    }

    /// Builds the node.
    pub fn build(self) -> Node {
        Node::new(self.labels, JsonValue::Object(self.properties))
    }
}

impl Default for NodeBuilder {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_node_id() {
        let id1 = NodeId::new();
        let id2 = NodeId::new();
        assert_ne!(id1, id2);

        let hex = id1.to_hex();
        let id3 = NodeId::from_hex(&hex).unwrap();
        assert_eq!(id1, id3);
    }

    #[test]
    fn test_node_creation() {
        let node = Node::new(
            vec!["User".to_string()],
            serde_json::json!({"name": "Alice", "age": 30}),
        );

        assert!(node.has_label("User"));
        assert!(!node.has_label("Admin"));
        assert_eq!(
            node.get_property("name"),
            Some(&serde_json::json!("Alice"))
        );
    }

    #[test]
    fn test_node_builder() {
        let node = NodeBuilder::new()
            .label("Person")
            .label("Developer")
            .property("name", "Bob")
            .property("skills", serde_json::json!(["Rust", "Python"]))
            .build();

        assert!(node.has_label("Person"));
        assert!(node.has_label("Developer"));
        assert_eq!(node.get_property("name"), Some(&serde_json::json!("Bob")));
    }

    #[test]
    fn test_node_labels() {
        let mut node = Node::new(vec!["User".to_string()], serde_json::json!({}));

        node.add_label("Admin");
        assert!(node.has_label("Admin"));

        node.remove_label("Admin");
        assert!(!node.has_label("Admin"));
    }

    #[test]
    fn test_node_properties() {
        let mut node = Node::new(vec![], serde_json::json!({}));

        node.set_property("key", serde_json::json!("value"));
        assert_eq!(node.get_property("key"), Some(&serde_json::json!("value")));

        let removed = node.remove_property("key");
        assert_eq!(removed, Some(serde_json::json!("value")));
        assert_eq!(node.get_property("key"), None);
    }

    #[test]
    fn test_node_matches() {
        let node = Node::new(
            vec!["User".to_string()],
            serde_json::json!({"name": "Alice", "age": 30, "active": true}),
        );

        assert!(node.matches_properties(&serde_json::json!({"name": "Alice"})));
        assert!(node.matches_properties(&serde_json::json!({"name": "Alice", "age": 30})));
        assert!(!node.matches_properties(&serde_json::json!({"name": "Bob"})));
    }
}
