//! Graph partitioning strategies.
//!
//! This module defines the `GraphPartitioner` trait and implements multiple
//! partitioning strategies for distributing graph data across pods.

use crate::distributed::partition::metadata::{PartitionMap, PartitionQualityMetrics};
use crate::graph::PropertyGraph;
use crate::error::CypherResult;
use serde::{Deserialize, Serialize};

/// Configuration for graph partitioning.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartitionConfig {
    /// Number of partitions to create
    pub num_partitions: u32,

    /// Strategy-specific options (JSON-serializable)
    #[serde(default)]
    pub options: std::collections::HashMap<String, String>,
}

impl PartitionConfig {
    /// Create a new partition configuration.
    pub fn new(num_partitions: u32) -> Self {
        Self {
            num_partitions,
            options: std::collections::HashMap::new(),
        }
    }

    /// Set a configuration option.
    pub fn with_option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
        self.options.insert(key.into(), value.into());
        self
    }
}

/// Trait for graph partitioning strategies.
pub trait GraphPartitioner: Send + Sync {
    /// Partition a graph according to this strategy.
    ///
    /// # Arguments
    ///
    /// * `graph` - The graph to partition
    /// * `config` - Partitioning configuration
    ///
    /// # Returns
    ///
    /// A `PartitionMap` that assigns each vertex and edge to a partition.
    fn partition(&self, graph: &PropertyGraph, config: &PartitionConfig) -> CypherResult<PartitionMap>;

    /// Estimate partition quality without actually partitioning.
    ///
    /// This is useful for comparing strategies without executing them.
    fn estimate_quality(&self, partition_map: &PartitionMap) -> PartitionQualityMetrics {
        partition_map.calculate_quality_metrics()
    }

    /// Check if a partition map needs rebalancing.
    fn needs_rebalancing(&self, metrics: &PartitionQualityMetrics) -> bool {
        metrics.needs_rebalancing()
    }

    /// Get the name of this partitioning strategy.
    fn strategy_name(&self) -> &str;
}

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

    #[test]
    fn test_partition_config() {
        let config = PartitionConfig::new(3)
            .with_option("seed", "42")
            .with_option("algorithm", "hash");

        assert_eq!(config.num_partitions, 3);
        assert_eq!(config.options.get("seed"), Some(&"42".to_string()));
        assert_eq!(config.options.get("algorithm"), Some(&"hash".to_string()));
    }
}
