// Copyright 2026 Gregorio Momm
// Licensed under the Apache License, Version 2.0
//
// This file implements GraphBackend for graphrs (community detection library).
// graphrs: https://github.com/malcolmvr/graphrs (MIT License)
//
// Built on petgraph (same as PropertyGraph and RustworkxCore) with community detection algorithms.

//! Graphrs backend - Community detection algorithms (Louvain, Leiden)

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

use indexmap::IndexMap;
use petgraph::stable_graph::{EdgeIndex, NodeIndex, StableDiGraph};
use petgraph::visit::EdgeRef;
use petgraph::Direction;

use crate::graph::backend::GraphBackend;
use crate::graph::types::{GraphEdge, GraphNode, Path, PropertyValue};
use crate::error::{ExecutionError, ExecutionResult};

/// A property graph backed by graphrs (petgraph + community detection algorithms).
#[derive(Debug)]
pub struct GraphrsBackend {
    /// The underlying graph structure (same as PropertyGraph).
    pub(crate) graph: StableDiGraph<GraphNode, GraphEdge>,
    /// Index from node ID to graph node index.
    node_id_index: HashMap<u64, NodeIndex>,
    /// Index from edge ID to graph edge index.
    edge_id_index: HashMap<u64, EdgeIndex>,
    /// Index from label to set of node indices.
    label_index: HashMap<String, HashSet<NodeIndex>>,
    /// Index from relationship type to set of edge indices.
    rel_type_index: HashMap<String, HashSet<EdgeIndex>>,
    /// Next available node ID.
    next_node_id: u64,
    /// Next available edge ID.
    next_edge_id: u64,
}

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

impl GraphrsBackend {
    /// Create a new empty graph.
    pub fn new() -> Self {
        Self {
            graph: StableDiGraph::new(),
            node_id_index: HashMap::new(),
            edge_id_index: HashMap::new(),
            label_index: HashMap::new(),
            rel_type_index: HashMap::new(),
            next_node_id: 1,
            next_edge_id: 1,
        }
    }

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

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

    /// Create a new node with the given labels and properties.
    pub fn create_node(
        &mut self,
        labels: impl IntoIterator<Item = impl Into<String>>,
        properties: IndexMap<String, PropertyValue>,
    ) -> &GraphNode {
        let id = self.next_node_id;
        self.next_node_id += 1;

        let mut node = GraphNode::new(id);
        node.properties = properties;

        for label in labels {
            let label = label.into();
            node.labels.insert(label.clone());
        }

        let idx = self.graph.add_node(node);
        self.node_id_index.insert(id, idx);

        // Update label index
        let node = &self.graph[idx];
        for label in &node.labels {
            self.label_index
                .entry(label.clone())
                .or_default()
                .insert(idx);
        }

        &self.graph[idx]
    }

    /// Create a new relationship between two nodes.
    pub fn create_relationship(
        &mut self,
        start_id: u64,
        end_id: u64,
        rel_type: impl Into<String>,
        properties: IndexMap<String, PropertyValue>,
    ) -> ExecutionResult<&GraphEdge> {
        let start_idx = self
            .node_id_index
            .get(&start_id)
            .copied()
            .ok_or(ExecutionError::NodeNotFound(start_id))?;
        let end_idx = self
            .node_id_index
            .get(&end_id)
            .copied()
            .ok_or(ExecutionError::NodeNotFound(end_id))?;

        let id = self.next_edge_id;
        self.next_edge_id += 1;

        let rel_type = rel_type.into();
        let mut edge = GraphEdge::new(id, rel_type.clone());
        edge.properties = properties;

        let edge_idx = self.graph.add_edge(start_idx, end_idx, edge);
        self.edge_id_index.insert(id, edge_idx);

        // Update rel type index
        self.rel_type_index
            .entry(rel_type)
            .or_default()
            .insert(edge_idx);

        Ok(&self.graph[edge_idx])
    }

    /// Insert a node with a pre-assigned ID (for WAL replay / partition restore).
    pub fn add_existing_node(&mut self, node: GraphNode) {
        let id = node.id;
        let idx = self.graph.add_node(node);
        self.node_id_index.insert(id, idx);
        let node = &self.graph[idx];
        for label in &node.labels {
            self.label_index
                .entry(label.clone())
                .or_default()
                .insert(idx);
        }
        if id >= self.next_node_id {
            self.next_node_id = id + 1;
        }
    }

    /// Create a relationship with a caller-supplied edge ID (for WAL replay).
    pub 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> {
        let start_idx = self.node_id_index.get(&start_id).copied()
            .ok_or(ExecutionError::NodeNotFound(start_id))?;
        let end_idx = self.node_id_index.get(&end_id).copied()
            .ok_or(ExecutionError::NodeNotFound(end_id))?;
        if edge_id >= self.next_edge_id {
            self.next_edge_id = edge_id + 1;
        }
        let rel_type = rel_type.into();
        let mut edge = GraphEdge::new(edge_id, rel_type.clone());
        edge.properties = properties;
        let edge_idx = self.graph.add_edge(start_idx, end_idx, edge);
        self.edge_id_index.insert(edge_id, edge_idx);
        self.rel_type_index.entry(rel_type).or_default().insert(edge_idx);
        Ok(edge_id)
    }

    /// Get a node by its ID.
    pub fn get_node(&self, id: u64) -> Option<&GraphNode> {
        self.node_id_index
            .get(&id)
            .and_then(|idx| self.graph.node_weight(*idx))
    }

    /// Get a mutable node by its ID.
    pub fn get_node_mut(&mut self, id: u64) -> Option<&mut GraphNode> {
        self.node_id_index
            .get(&id)
            .copied()
            .and_then(move |idx| self.graph.node_weight_mut(idx))
    }

    /// Count the number of relationships connected to a node (both incoming and outgoing).
    pub fn count_node_relationships(&self, id: u64) -> usize {
        if let Some(node_idx) = self.node_id_index.get(&id).copied() {
            self.graph.edges_directed(node_idx, Direction::Outgoing).count()
                + self.graph.edges_directed(node_idx, Direction::Incoming).count()
        } else {
            0
        }
    }

    /// Get an edge by its ID.
    pub fn get_edge(&self, id: u64) -> Option<&GraphEdge> {
        self.edge_id_index
            .get(&id)
            .and_then(|idx| self.graph.edge_weight(*idx))
    }

    /// Get a mutable edge by its ID.
    pub fn get_edge_mut(&mut self, id: u64) -> Option<&mut GraphEdge> {
        self.edge_id_index
            .get(&id)
            .copied()
            .and_then(move |idx| self.graph.edge_weight_mut(idx))
    }

    /// Get the start and end node IDs of a relationship.
    pub fn get_edge_endpoints(&self, edge_id: u64) -> Option<(u64, u64)> {
        let edge_idx = self.edge_id_index.get(&edge_id)?;
        let (start, end) = self.graph.edge_endpoints(*edge_idx)?;
        Some((self.graph[start].id, self.graph[end].id))
    }

    /// Get all nodes in the graph.
    pub fn all_nodes(&self) -> impl Iterator<Item = &GraphNode> {
        self.graph.node_weights()
    }

    /// Get all edges in the graph.
    pub fn all_edges(&self) -> impl Iterator<Item = &GraphEdge> {
        self.graph.edge_weights()
    }

    /// Get all nodes with a specific label.
    pub fn nodes_with_label(&self, label: &str) -> Vec<&GraphNode> {
        self.label_index
            .get(label)
            .map(|indices| {
                indices
                    .iter()
                    .filter_map(|idx| self.graph.node_weight(*idx))
                    .collect()
            })
            .unwrap_or_default()
    }

    /// Get all edges with a specific type.
    pub fn edges_with_type(&self, rel_type: &str) -> Vec<&GraphEdge> {
        self.rel_type_index
            .get(rel_type)
            .map(|indices| {
                indices
                    .iter()
                    .filter_map(|idx| self.graph.edge_weight(*idx))
                    .collect()
            })
            .unwrap_or_default()
    }

    /// Get outgoing edges from a node.
    pub fn outgoing_edges(&self, node_id: u64) -> Vec<(u64, &GraphEdge)> {
        let Some(node_idx) = self.node_id_index.get(&node_id) else {
            return vec![];
        };

        self.graph
            .edges_directed(*node_idx, Direction::Outgoing)
            .map(|edge_ref| {
                let target = self.graph[edge_ref.target()].id;
                (target, edge_ref.weight())
            })
            .collect()
    }

    /// Get incoming edges to a node.
    pub fn incoming_edges(&self, node_id: u64) -> Vec<(u64, &GraphEdge)> {
        let Some(node_idx) = self.node_id_index.get(&node_id) else {
            return vec![];
        };

        self.graph
            .edges_directed(*node_idx, Direction::Incoming)
            .map(|edge_ref| {
                let source = self.graph[edge_ref.source()].id;
                (source, edge_ref.weight())
            })
            .collect()
    }

    /// Get all edges connected to a node (both incoming and outgoing).
    pub fn all_edges_for_node(&self, node_id: u64) -> Vec<(u64, u64, &GraphEdge)> {
        let Some(node_idx) = self.node_id_index.get(&node_id) else {
            return vec![];
        };

        let mut edges = Vec::new();

        for edge_ref in self.graph.edges_directed(*node_idx, Direction::Outgoing) {
            let source = self.graph[edge_ref.source()].id;
            let target = self.graph[edge_ref.target()].id;
            edges.push((source, target, edge_ref.weight()));
        }

        for edge_ref in self.graph.edges_directed(*node_idx, Direction::Incoming) {
            let source = self.graph[edge_ref.source()].id;
            let target = self.graph[edge_ref.target()].id;
            edges.push((source, target, edge_ref.weight()));
        }

        edges
    }

    /// Delete a node by ID. Returns error if the node has relationships.
    pub fn delete_node(&mut self, id: u64) -> ExecutionResult<GraphNode> {
        let node_idx = self
            .node_id_index
            .get(&id)
            .copied()
            .ok_or(ExecutionError::NodeNotFound(id))?;

        // Check for relationships
        let has_edges = self.graph.edges(node_idx).next().is_some();
        if has_edges {
            return Err(ExecutionError::ConstraintViolation(format!(
                "Cannot delete node {} because it still has relationships. Use DETACH DELETE to remove relationships.",
                id
            )));
        }

        // Remove from label index
        if let Some(node) = self.graph.node_weight(node_idx) {
            for label in &node.labels {
                if let Some(set) = self.label_index.get_mut(label) {
                    set.remove(&node_idx);
                }
            }
        }

        // Remove from graph
        let node = self.graph.remove_node(node_idx).unwrap();
        self.node_id_index.remove(&id);

        Ok(node)
    }

    /// Delete a node and all its relationships.
    pub fn detach_delete_node(&mut self, id: u64) -> ExecutionResult<GraphNode> {
        let node_idx = self
            .node_id_index
            .get(&id)
            .copied()
            .ok_or(ExecutionError::NodeNotFound(id))?;

        // Collect all edges to delete
        let edges_to_delete: Vec<EdgeIndex> = self
            .graph
            .edges_directed(node_idx, Direction::Outgoing)
            .chain(self.graph.edges_directed(node_idx, Direction::Incoming))
            .map(|e| e.id())
            .collect();

        // Delete edges
        for edge_idx in edges_to_delete {
            if let Some(edge) = self.graph.edge_weight(edge_idx) {
                let edge_id = edge.id;
                let rel_type = edge.rel_type.clone();
                self.edge_id_index.remove(&edge_id);
                if let Some(set) = self.rel_type_index.get_mut(&rel_type) {
                    set.remove(&edge_idx);
                }
            }
            self.graph.remove_edge(edge_idx);
        }

        // Remove from label index
        if let Some(node) = self.graph.node_weight(node_idx) {
            for label in &node.labels {
                if let Some(set) = self.label_index.get_mut(label) {
                    set.remove(&node_idx);
                }
            }
        }

        // Remove from graph
        let node = self.graph.remove_node(node_idx).unwrap();
        self.node_id_index.remove(&id);

        Ok(node)
    }

    /// Delete a relationship by ID.
    pub fn delete_relationship(&mut self, id: u64) -> ExecutionResult<GraphEdge> {
        let edge_idx = self
            .edge_id_index
            .get(&id)
            .copied()
            .ok_or(ExecutionError::RelationshipNotFound(id))?;

        let edge = self.graph.edge_weight(edge_idx).cloned().unwrap();

        // Remove from type index
        if let Some(set) = self.rel_type_index.get_mut(&edge.rel_type) {
            set.remove(&edge_idx);
        }

        // Remove from graph
        self.graph.remove_edge(edge_idx);
        self.edge_id_index.remove(&id);

        Ok(edge)
    }

    /// Add a label to a node.
    pub fn add_label(&mut self, node_id: u64, label: impl Into<String>) -> ExecutionResult<()> {
        let node_idx = self
            .node_id_index
            .get(&node_id)
            .copied()
            .ok_or(ExecutionError::NodeNotFound(node_id))?;

        let label = label.into();

        if let Some(node) = self.graph.node_weight_mut(node_idx) {
            node.labels.insert(label.clone());
        }

        self.label_index.entry(label).or_default().insert(node_idx);

        Ok(())
    }

    /// Remove a label from a node.
    pub fn remove_label(&mut self, node_id: u64, label: &str) -> ExecutionResult<bool> {
        let node_idx = self
            .node_id_index
            .get(&node_id)
            .copied()
            .ok_or(ExecutionError::NodeNotFound(node_id))?;

        let removed = if let Some(node) = self.graph.node_weight_mut(node_idx) {
            node.labels.remove(label)
        } else {
            false
        };

        if removed {
            if let Some(set) = self.label_index.get_mut(label) {
                set.remove(&node_idx);
            }
        }

        Ok(removed)
    }

    /// Set a property on a node.
    pub fn set_node_property(
        &mut self,
        node_id: u64,
        key: impl Into<String>,
        value: PropertyValue,
    ) -> ExecutionResult<()> {
        let node = self
            .get_node_mut(node_id)
            .ok_or(ExecutionError::NodeNotFound(node_id))?;
        node.properties.insert(key.into(), value);
        Ok(())
    }

    /// Remove a property from a node.
    pub fn remove_node_property(
        &mut self,
        node_id: u64,
        key: &str,
    ) -> ExecutionResult<Option<PropertyValue>> {
        let node = self
            .get_node_mut(node_id)
            .ok_or(ExecutionError::NodeNotFound(node_id))?;
        Ok(node.properties.shift_remove(key))
    }

    /// Set a property on a relationship.
    pub fn set_edge_property(
        &mut self,
        edge_id: u64,
        key: impl Into<String>,
        value: PropertyValue,
    ) -> ExecutionResult<()> {
        let edge = self
            .get_edge_mut(edge_id)
            .ok_or(ExecutionError::RelationshipNotFound(edge_id))?;
        edge.properties.insert(key.into(), value);
        Ok(())
    }

    /// Remove a property from a relationship.
    pub fn remove_edge_property(
        &mut self,
        edge_id: u64,
        key: &str,
    ) -> ExecutionResult<Option<PropertyValue>> {
        let edge = self
            .get_edge_mut(edge_id)
            .ok_or(ExecutionError::RelationshipNotFound(edge_id))?;
        Ok(edge.properties.shift_remove(key))
    }

    /// Find the shortest path between two nodes.
    pub fn shortest_path(&self, start_id: u64, end_id: u64) -> Option<Path> {
        use petgraph::algo::dijkstra;

        let start_idx = self.node_id_index.get(&start_id)?;
        let end_idx = self.node_id_index.get(&end_id)?;

        // Use dijkstra with uniform weight
        let predecessors = dijkstra(&self.graph, *start_idx, Some(*end_idx), |_| 1usize);

        if !predecessors.contains_key(end_idx) {
            return None;
        }

        // Reconstruct path using BFS to get actual predecessors
        use std::collections::VecDeque;
        let mut queue = VecDeque::new();
        let mut visited = HashSet::new();
        let mut parent: HashMap<NodeIndex, (NodeIndex, EdgeIndex)> = HashMap::new();

        queue.push_back(*start_idx);
        visited.insert(*start_idx);

        while let Some(current) = queue.pop_front() {
            if current == *end_idx {
                break;
            }

            for edge_ref in self.graph.edges(current) {
                let next = edge_ref.target();
                if !visited.contains(&next) {
                    visited.insert(next);
                    parent.insert(next, (current, edge_ref.id()));
                    queue.push_back(next);
                }
            }
        }

        if !parent.contains_key(end_idx) && start_idx != end_idx {
            return None;
        }

        // Build path
        let mut path_nodes = vec![*end_idx];
        let mut path_edges = vec![];
        let mut current = *end_idx;

        while let Some(&(prev, edge_idx)) = parent.get(&current) {
            path_nodes.push(prev);
            path_edges.push(edge_idx);
            current = prev;
        }

        path_nodes.reverse();
        path_edges.reverse();

        let mut path = Path::new(self.graph[path_nodes[0]].clone());
        for (i, edge_idx) in path_edges.iter().enumerate() {
            let edge = self.graph[*edge_idx].clone();
            let node = self.graph[path_nodes[i + 1]].clone();
            path.extend(edge, node);
        }

        Some(path)
    }

    /// Find all shortest paths between two nodes.
    pub fn all_shortest_paths(&self, start_id: u64, end_id: u64) -> Vec<Path> {
        use std::collections::VecDeque;

        let Some(start_idx) = self.node_id_index.get(&start_id) else {
            return vec![];
        };
        let Some(end_idx) = self.node_id_index.get(&end_id) else {
            return vec![];
        };

        if start_idx == end_idx {
            return vec![Path::new(self.graph[*start_idx].clone())];
        }

        // BFS to find shortest distance and all parents at that distance
        let mut queue = VecDeque::new();
        let mut distances: HashMap<NodeIndex, usize> = HashMap::new();
        let mut parents: HashMap<NodeIndex, Vec<(NodeIndex, EdgeIndex)>> = HashMap::new();

        queue.push_back(*start_idx);
        distances.insert(*start_idx, 0);

        while let Some(current) = queue.pop_front() {
            let current_dist = distances[&current];

            if let Some(&end_dist) = distances.get(end_idx) {
                if current_dist > end_dist {
                    break;
                }
            }

            for edge_ref in self.graph.edges(current) {
                let next = edge_ref.target();
                let next_dist = current_dist + 1;

                match distances.get(&next) {
                    None => {
                        distances.insert(next, next_dist);
                        parents.insert(next, vec![(current, edge_ref.id())]);
                        queue.push_back(next);
                    }
                    Some(&d) if d == next_dist => {
                        parents.get_mut(&next).unwrap().push((current, edge_ref.id()));
                    }
                    _ => {}
                }
            }
        }

        if !distances.contains_key(end_idx) {
            return vec![];
        }

        // Reconstruct all paths
        fn build_paths(
            graph: &StableDiGraph<GraphNode, GraphEdge>,
            parents: &HashMap<NodeIndex, Vec<(NodeIndex, EdgeIndex)>>,
            start: NodeIndex,
            current: NodeIndex,
            path: &mut Vec<(NodeIndex, Option<EdgeIndex>)>,
            all_paths: &mut Vec<Path>,
        ) {
            path.push((current, None));

            if current == start {
                // Build path from collected nodes
                let mut new_path = Path::new(graph[path.last().unwrap().0].clone());
                for i in (0..path.len() - 1).rev() {
                    if let Some(edge_idx) = path[i].1 {
                        let edge = graph[edge_idx].clone();
                        let node = graph[path[i].0].clone();
                        new_path.extend(edge, node);
                    }
                }
                all_paths.push(new_path);
            } else if let Some(parent_list) = parents.get(&current) {
                for &(parent, edge_idx) in parent_list {
                    path.last_mut().unwrap().1 = Some(edge_idx);
                    build_paths(graph, parents, start, parent, path, all_paths);
                }
            }

            path.pop();
        }

        let mut all_paths = Vec::new();
        let mut path = Vec::new();
        build_paths(
            &self.graph,
            &parents,
            *start_idx,
            *end_idx,
            &mut path,
            &mut all_paths,
        );

        all_paths
    }

    /// Clear all data from the graph.
    pub fn clear(&mut self) {
        self.graph.clear();
        self.node_id_index.clear();
        self.edge_id_index.clear();
        self.label_index.clear();
        self.rel_type_index.clear();
        self.next_node_id = 1;
        self.next_edge_id = 1;
    }
}

// === GraphBackend trait implementation ===

impl GraphBackend for GraphrsBackend {
    type NodeRef<'a> = &'a GraphNode;
    type EdgeRef<'a> = &'a GraphEdge;
    type NodesIter<'a> = Box<dyn Iterator<Item = &'a GraphNode> + 'a>;
    type EdgesIter<'a> = Box<dyn Iterator<Item = &'a GraphEdge> + 'a>;

    fn get_node(&self, id: u64) -> Option<Self::NodeRef<'_>> {
        self.get_node(id)
    }

    fn get_node_mut(&mut self, id: u64) -> Option<&mut GraphNode> {
        self.get_node_mut(id)
    }

    fn all_nodes(&self) -> Self::NodesIter<'_> {
        Box::new(self.all_nodes())
    }

    fn nodes_with_label(&self, label: &str) -> Vec<Self::NodeRef<'_>> {
        self.nodes_with_label(label)
    }

    fn get_edge(&self, id: u64) -> Option<Self::EdgeRef<'_>> {
        self.get_edge(id)
    }

    fn get_edge_mut(&mut self, id: u64) -> Option<&mut GraphEdge> {
        self.get_edge_mut(id)
    }

    fn all_edges(&self) -> Self::EdgesIter<'_> {
        Box::new(self.all_edges())
    }

    fn edges_with_type(&self, rel_type: &str) -> Vec<Self::EdgeRef<'_>> {
        self.edges_with_type(rel_type)
    }

    fn get_edge_endpoints(&self, edge_id: u64) -> Option<(u64, u64)> {
        self.get_edge_endpoints(edge_id)
    }

    fn outgoing_edges(&self, node_id: u64) -> Vec<(u64, Self::EdgeRef<'_>)> {
        self.outgoing_edges(node_id)
    }

    fn incoming_edges(&self, node_id: u64) -> Vec<(u64, Self::EdgeRef<'_>)> {
        self.incoming_edges(node_id)
    }

    fn all_edges_for_node(&self, node_id: u64) -> Vec<(u64, u64, Self::EdgeRef<'_>)> {
        self.all_edges_for_node(node_id)
    }

    fn count_node_relationships(&self, node_id: u64) -> usize {
        self.count_node_relationships(node_id)
    }

    fn shortest_path(&self, start_id: u64, end_id: u64) -> Option<Path> {
        self.shortest_path(start_id, end_id)
    }

    fn all_shortest_paths(&self, start_id: u64, end_id: u64) -> Vec<Path> {
        self.all_shortest_paths(start_id, end_id)
    }

    fn add_existing_node(&mut self, node: GraphNode) {
        self.add_existing_node(node);
    }

    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> {
        self.create_relationship_with_id(edge_id, start_id, end_id, rel_type, properties)
    }

    fn create_node(
        &mut self,
        labels: impl IntoIterator<Item = impl Into<String>>,
        properties: IndexMap<String, PropertyValue>,
    ) -> u64 {
        self.create_node(labels, properties).id
    }

    fn create_relationship(
        &mut self,
        start_id: u64,
        end_id: u64,
        rel_type: impl Into<String>,
        properties: IndexMap<String, PropertyValue>,
    ) -> ExecutionResult<u64> {
        self.create_relationship(start_id, end_id, rel_type, properties)
            .map(|edge| edge.id)
    }

    fn delete_node(&mut self, id: u64) -> ExecutionResult<GraphNode> {
        self.delete_node(id)
    }

    fn delete_relationship(&mut self, id: u64) -> ExecutionResult<GraphEdge> {
        self.delete_relationship(id)
    }

    fn detach_delete_node(&mut self, id: u64) -> ExecutionResult<GraphNode> {
        self.detach_delete_node(id)
    }

    fn set_node_property(
        &mut self,
        node_id: u64,
        key: impl Into<String>,
        value: PropertyValue,
    ) -> ExecutionResult<()> {
        self.set_node_property(node_id, key, value)
    }

    fn set_edge_property(
        &mut self,
        edge_id: u64,
        key: impl Into<String>,
        value: PropertyValue,
    ) -> ExecutionResult<()> {
        self.set_edge_property(edge_id, key, value)
    }

    fn remove_node_property(
        &mut self,
        node_id: u64,
        key: &str,
    ) -> ExecutionResult<Option<PropertyValue>> {
        self.remove_node_property(node_id, key)
    }

    fn remove_edge_property(
        &mut self,
        edge_id: u64,
        key: &str,
    ) -> ExecutionResult<Option<PropertyValue>> {
        self.remove_edge_property(edge_id, key)
    }

    fn add_label(&mut self, node_id: u64, label: impl Into<String>) -> ExecutionResult<()> {
        self.add_label(node_id, label)
    }

    fn remove_label(&mut self, node_id: u64, label: &str) -> ExecutionResult<bool> {
        self.remove_label(node_id, label)
    }

    fn node_count(&self) -> usize {
        self.node_count()
    }

    fn edge_count(&self) -> usize {
        self.edge_count()
    }
}
