//! Pluggable graph backend trait for zero-cost abstraction over different graph storage implementations.

use std::collections::HashSet;

use indexmap::IndexMap;

use super::types::{GraphEdge, GraphNode, PropertyValue};
use crate::error::ExecutionResult;

/// Trait for graph node references.
///
/// This trait abstracts over node representations in different backends,
/// allowing zero-cost iteration through GATs.
pub trait NodeLike {
    /// Get the unique ID of this node.
    fn id(&self) -> u64;

    /// Get the labels attached to this node.
    fn labels(&self) -> &HashSet<String>;

    /// Get the properties of this node.
    fn properties(&self) -> &IndexMap<String, PropertyValue>;
}

/// Trait for graph edge references.
///
/// This trait abstracts over edge representations in different backends,
/// allowing zero-cost iteration through GATs.
pub trait EdgeLike {
    /// Get the unique ID of this edge.
    fn id(&self) -> u64;

    /// Get the relationship type.
    fn rel_type(&self) -> &str;

    /// Get the properties of this edge.
    fn properties(&self) -> &IndexMap<String, PropertyValue>;
}

/// Pluggable graph backend trait using Generic Associated Types (GATs).
///
/// This trait provides a zero-cost abstraction over different graph storage backends.
/// Through monomorphization, there is no runtime overhead when using a concrete backend.
///
/// # Design Goals
///
/// - **Zero-cost abstraction**: All methods compile to direct calls through monomorphization
/// - **Flexible iteration**: GATs allow backends to return their own iterator types
/// - **Property graph support**: Full support for labels, types, and properties
/// - **Transaction safety**: Clear ownership and lifetime requirements
///
/// # Type Parameters
///
/// - `NodeRef<'a>`: Reference type for nodes (e.g., `&'a GraphNode` or custom wrapper)
/// - `EdgeRef<'a>`: Reference type for edges (e.g., `&'a GraphEdge` or custom wrapper)
/// - `NodesIter<'a>`: Iterator type for node collections
/// - `EdgesIter<'a>`: Iterator type for edge collections
pub trait GraphBackend: Sized {
    /// Reference type for nodes in this backend.
    type NodeRef<'a>: NodeLike
    where
        Self: 'a;

    /// Reference type for edges in this backend.
    type EdgeRef<'a>: EdgeLike
    where
        Self: 'a;

    /// Iterator type for node collections.
    /// Using Box for stable Rust compatibility.
    type NodesIter<'a>: Iterator<Item = Self::NodeRef<'a>>
    where
        Self: 'a;

    /// Iterator type for edge collections.
    /// Using Box for stable Rust compatibility.
    type EdgesIter<'a>: Iterator<Item = Self::EdgeRef<'a>>
    where
        Self: 'a;

    // === Core Read Operations ===

    /// Get a node by its ID.
    ///
    /// Returns `None` if the node does not exist.
    fn get_node(&self, id: u64) -> Option<Self::NodeRef<'_>>;

    /// Get a mutable reference to a node by its ID.
    ///
    /// Returns `None` if the node does not exist.
    fn get_node_mut(&mut self, id: u64) -> Option<&mut GraphNode>;

    /// Get all nodes in the graph.
    fn all_nodes(&self) -> Self::NodesIter<'_>;

    /// Get all nodes with a specific label.
    ///
    /// The implementation should optimize this using label indices.
    fn nodes_with_label(&self, label: &str) -> Vec<Self::NodeRef<'_>>;

    /// Get an edge by its ID.
    ///
    /// Returns `None` if the edge does not exist.
    fn get_edge(&self, id: u64) -> Option<Self::EdgeRef<'_>>;

    /// Get a mutable reference to an edge by its ID.
    ///
    /// Returns `None` if the edge does not exist.
    fn get_edge_mut(&mut self, id: u64) -> Option<&mut GraphEdge>;

    /// Get all edges in the graph.
    fn all_edges(&self) -> Self::EdgesIter<'_>;

    /// Get all edges with a specific relationship type.
    ///
    /// The implementation should optimize this using type indices.
    fn edges_with_type(&self, rel_type: &str) -> Vec<Self::EdgeRef<'_>>;

    /// Get the start and end node IDs of an edge.
    ///
    /// Returns `(start_id, end_id)` or `None` if the edge does not exist.
    fn get_edge_endpoints(&self, edge_id: u64) -> Option<(u64, u64)>;

    /// Get outgoing edges from a node.
    ///
    /// Returns a vector of `(target_node_id, edge_ref)` pairs.
    fn outgoing_edges(&self, node_id: u64) -> Vec<(u64, Self::EdgeRef<'_>)>;

    /// Get incoming edges to a node.
    ///
    /// Returns a vector of `(source_node_id, edge_ref)` pairs.
    fn incoming_edges(&self, node_id: u64) -> Vec<(u64, Self::EdgeRef<'_>)>;

    /// Get all edges connected to a node (both incoming and outgoing).
    ///
    /// Returns a vector of `(source_id, target_id, edge_ref)` tuples.
    fn all_edges_for_node(&self, node_id: u64) -> Vec<(u64, u64, Self::EdgeRef<'_>)>;

    /// Count the number of relationships connected to a node (both incoming and outgoing).
    fn count_node_relationships(&self, node_id: u64) -> usize;

    // === Path Finding ===

    /// Find the shortest path between two nodes.
    ///
    /// Returns `None` if there is no path between the nodes.
    fn shortest_path(&self, start_id: u64, end_id: u64) -> Option<crate::graph::Path>;

    /// Find all shortest paths between two nodes.
    ///
    /// Returns an empty vector if there are no paths between the nodes.
    fn all_shortest_paths(&self, start_id: u64, end_id: u64) -> Vec<crate::graph::Path>;

    // === Core Write Operations ===

    /// Insert a node that already has a pre-assigned ID.
    ///
    /// Used by WAL replay and the partitioned backend to restore nodes with
    /// their original IDs. The backend must honour the given ID and advance
    /// its internal counter so future auto-allocated IDs don't collide.
    fn add_existing_node(&mut self, node: GraphNode);

    /// Create a relationship with a caller-supplied edge ID.
    ///
    /// Used by WAL replay so that restored edges keep their original IDs.
    /// Returns the edge ID on success.
    fn create_relationship_with_id(
        &mut self,
        edge_id: u64,
        start_id: u64,
        end_id: u64,
        rel_type: impl Into<String>,
        properties: IndexMap<String, PropertyValue>,
    ) -> ExecutionResult<u64>;

    /// Create a new node with the given labels and properties.
    ///
    /// Returns the ID of the newly created node.
    fn create_node(
        &mut self,
        labels: impl IntoIterator<Item = impl Into<String>>,
        properties: IndexMap<String, PropertyValue>,
    ) -> u64;

    /// Create a new relationship between two nodes.
    ///
    /// Returns the ID of the newly created relationship, or an error if either node does not exist.
    fn create_relationship(
        &mut self,
        start_id: u64,
        end_id: u64,
        rel_type: impl Into<String>,
        properties: IndexMap<String, PropertyValue>,
    ) -> ExecutionResult<u64>;

    /// Delete a node from the graph.
    ///
    /// Returns the deleted node, or an error if:
    /// - The node does not exist
    /// - The node has attached relationships
    ///
    /// Note: Nodes with relationships must have those relationships deleted first.
    fn delete_node(&mut self, id: u64) -> ExecutionResult<GraphNode>;

    /// Delete a relationship from the graph.
    ///
    /// Returns the deleted relationship, or an error if the relationship does not exist.
    fn delete_relationship(&mut self, id: u64) -> ExecutionResult<GraphEdge>;

    /// Delete a node and all its relationships.
    ///
    /// Returns the deleted node, or an error if the node does not exist.
    fn detach_delete_node(&mut self, id: u64) -> ExecutionResult<GraphNode>;

    // === Property and Label Operations ===

    /// Set a property on a node.
    fn set_node_property(
        &mut self,
        node_id: u64,
        key: impl Into<String>,
        value: PropertyValue,
    ) -> ExecutionResult<()>;

    /// Set a property on a relationship.
    fn set_edge_property(
        &mut self,
        edge_id: u64,
        key: impl Into<String>,
        value: PropertyValue,
    ) -> ExecutionResult<()>;

    /// Remove a property from a node.
    ///
    /// Returns the previous value if the property existed.
    fn remove_node_property(
        &mut self,
        node_id: u64,
        key: &str,
    ) -> ExecutionResult<Option<PropertyValue>>;

    /// Remove a property from a relationship.
    ///
    /// Returns the previous value if the property existed.
    fn remove_edge_property(
        &mut self,
        edge_id: u64,
        key: &str,
    ) -> ExecutionResult<Option<PropertyValue>>;

    /// Add a label to a node.
    fn add_label(&mut self, node_id: u64, label: impl Into<String>) -> ExecutionResult<()>;

    /// Remove a label from a node.
    ///
    /// Returns `true` if the label was present and removed, `false` otherwise.
    fn remove_label(&mut self, node_id: u64, label: &str) -> ExecutionResult<bool>;

    // === Metadata ===

    /// Get the number of nodes in the graph.
    fn node_count(&self) -> usize;

    /// Get the number of edges in the graph.
    fn edge_count(&self) -> usize;

    // === Schema — indexes and constraints ===

    /// Check whether the given labels + properties would violate any active constraints.
    ///
    /// Returns `Err(ConstraintViolation)` if a UNIQUE constraint is violated.
    /// Default implementation: no constraints, always Ok.
    fn check_node_constraints(
        &self,
        _labels: &[String],
        _properties: &IndexMap<String, PropertyValue>,
    ) -> crate::error::ExecutionResult<()> {
        Ok(())
    }

    /// Create a label-property index.
    ///
    /// After this call, `nodes_with_label_property` may be served from the
    /// index rather than a full scan.  Backends that do not support indexes
    /// may implement this as a no-op.
    fn create_index(&mut self, _label: &str, _property: &str) {}

    /// Drop a label-property index.
    fn drop_index(&mut self, _label: &str, _property: &str) {}

    /// Register a UNIQUE constraint on (label, property).
    ///
    /// After registration, `create_node` calls that set a duplicate value for
    /// the indexed property must return `ConstraintViolation`.
    fn create_unique_constraint(&mut self, _label: &str, _property: &str) {}

    /// Drop a UNIQUE constraint.
    fn drop_unique_constraint(&mut self, _label: &str, _property: &str) {}

    /// Return all nodes that carry the given label AND have the specified
    /// property equal to `value`.
    ///
    /// Default implementation: full scan through `nodes_with_label`.
    fn nodes_with_label_property<'a>(
        &'a self,
        label: &str,
        property: &str,
        value: &PropertyValue,
    ) -> Vec<Self::NodeRef<'a>> {
        self.nodes_with_label(label)
            .into_iter()
            .filter(|n| n.properties().get(property) == Some(value))
            .collect()
    }
}

/// Implement NodeLike for GraphNode.
impl NodeLike for GraphNode {
    fn id(&self) -> u64 {
        self.id
    }

    fn labels(&self) -> &HashSet<String> {
        &self.labels
    }

    fn properties(&self) -> &IndexMap<String, PropertyValue> {
        &self.properties
    }
}

/// Implement NodeLike for references to GraphNode.
impl NodeLike for &GraphNode {
    fn id(&self) -> u64 {
        self.id
    }

    fn labels(&self) -> &HashSet<String> {
        &self.labels
    }

    fn properties(&self) -> &IndexMap<String, PropertyValue> {
        &self.properties
    }
}

/// Implement EdgeLike for GraphEdge.
impl EdgeLike for GraphEdge {
    fn id(&self) -> u64 {
        self.id
    }

    fn rel_type(&self) -> &str {
        &self.rel_type
    }

    fn properties(&self) -> &IndexMap<String, PropertyValue> {
        &self.properties
    }
}

/// Implement EdgeLike for references to GraphEdge.
impl EdgeLike for &GraphEdge {
    fn id(&self) -> u64 {
        self.id
    }

    fn rel_type(&self) -> &str {
        &self.rel_type
    }

    fn properties(&self) -> &IndexMap<String, PropertyValue> {
        &self.properties
    }
}
