// Copyright 2026 Gregorio Momm
// Licensed under the Apache License, Version 2.0

//! Unified algorithm trait for all graph backends.
//!
//! `AlgorithmEngine` provides a single trait that all four graph backends
//! (`NetworKitRustBackend`, `PropertyGraph`, `RustworkxCoreBackend`, `GraphrsBackend`)
//! implement, exposing a uniform API for graph algorithms regardless of the underlying
//! storage implementation.
//!
//! ## Design
//!
//! All return types use stable `u64` node IDs (never internal backend-specific IDs).
//! Set-valued results use `HashSet`; ordered/sequence results use `Vec`.
//!
//! ## Categories
//! - Centrality: PageRank, Betweenness, Closeness, Degree
//! - Pathfinding: BFS, Dijkstra, A*, Bellman-Ford, Floyd-Warshall
//! - Spanning Trees: Minimum, Maximum
//! - DAG: topological sort, is_dag, cycles, longest path, transitive closure
//! - Flow: max flow, min cut
//! - Coloring: vertex coloring, edge coloring, chromatic number
//! - Matching: max weight, max cardinality
//! - Community: Louvain, label propagation, Girvan-Newman
//! - Components: weakly connected, strongly connected
//! - Euler: has circuit/path, find circuit/path
//! - Planarity: is_planar
//!
//! ## Usage
//!
//! ```rust,no_run
//! use ocg::graph::{GraphBackend, PropertyGraph};
//! use ocg::graph::algorithm_engine::AlgorithmEngine;
//! use indexmap::IndexMap;
//!
//! let mut g = PropertyGraph::new();
//! let a = g.create_node(["A"], IndexMap::new()).id;
//! let b = g.create_node(["B"], IndexMap::new()).id;
//! g.create_relationship(a, b, "E", IndexMap::new()).unwrap();
//!
//! let pr = g.pagerank(0.85, 100);
//! let path = g.bfs_path(a, b);
//! assert!(g.is_dag());
//! ```

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

pub use crate::graph::backends::petgraph_algorithms::CommunityInfo;

/// Return type for max-flow: `Some((max_flow_value, per_edge_flows))` or `None`.
pub type MaxFlowResult = Option<(f64, HashMap<(u64, u64), f64>)>;

/// Unified algorithm interface for all graph backends.
///
/// Every method takes `&self` (read-only) and returns results keyed by stable `u64` node IDs.
pub trait AlgorithmEngine {
    // ── Centrality ─────────────────────────────────────────────────────────

    /// Degree centrality: normalized out-degree for each node.
    fn degree_centrality(&self) -> HashMap<u64, f64>;

    /// Betweenness centrality (Brandes algorithm).
    ///
    /// When `normalized` is true, divides by the number of pairs.
    fn betweenness_centrality(&self, normalized: bool) -> HashMap<u64, f64>;

    /// Closeness centrality.
    ///
    /// When `normalized` is true, scales by `(n-1) / max_dist`.
    fn closeness_centrality(&self, normalized: bool) -> HashMap<u64, f64>;

    /// PageRank with teleportation probability `1 - damping`.
    fn pagerank(&self, damping: f64, max_iter: usize) -> HashMap<u64, f64>;

    // ── Pathfinding ────────────────────────────────────────────────────────

    /// BFS shortest path (hop count) from `source` to `target`.
    ///
    /// Returns the node sequence `[source, ..., target]`, or `None` if unreachable.
    fn bfs_path(&self, source: u64, target: u64) -> Option<Vec<u64>>;

    /// Dijkstra shortest path (weighted) from `source` to `target`.
    ///
    /// Returns `Some((cost, path))` or `None` if unreachable.
    fn dijkstra_path(&self, source: u64, target: u64) -> Option<(f64, Vec<u64>)>;

    /// A* shortest path with caller-supplied heuristic.
    fn astar_path<F>(&self, source: u64, target: u64, heuristic: F) -> Option<(f64, Vec<u64>)>
    where
        F: Fn(u64) -> f64;

    /// Bellman-Ford single-source distances (handles negative edges).
    ///
    /// Returns `None` if a negative cycle is reachable from `source`.
    fn bellman_ford_distances(&self, source: u64) -> Option<HashMap<u64, f64>>;

    /// Floyd-Warshall all-pairs shortest distances.
    fn floyd_warshall_distances(&self) -> HashMap<(u64, u64), f64>;

    /// Dijkstra all-pairs shortest distances.
    fn all_pairs_distances(&self) -> HashMap<u64, HashMap<u64, f64>>;

    // ── Spanning Trees ─────────────────────────────────────────────────────

    /// Kruskal minimum spanning tree edges.
    ///
    /// Returns `Vec<(src, dst, weight)>`.
    fn minimum_spanning_tree(&self) -> Vec<(u64, u64, f64)>;

    /// Kruskal maximum spanning tree edges.
    ///
    /// Returns `Vec<(src, dst, weight)>`.
    fn maximum_spanning_tree(&self) -> Vec<(u64, u64, f64)>;

    // ── DAG Algorithms ─────────────────────────────────────────────────────

    /// Kahn's topological sort.
    ///
    /// Returns `Ok(order)` or `Err(cycle_nodes)` if a cycle is detected.
    fn topological_sort(&self) -> Result<Vec<u64>, Vec<u64>>;

    /// Returns `true` if the graph has no cycles.
    fn is_dag(&self) -> bool;

    /// Find all simple cycles (Johnson's algorithm).
    fn find_cycles(&self) -> Vec<Vec<u64>>;

    /// Longest path in a DAG (by hop count).
    ///
    /// Returns `None` if the graph is not a DAG.
    fn dag_longest_path(&self) -> Option<(usize, Vec<u64>)>;

    /// Longest path in a DAG (by edge weight sum).
    ///
    /// Returns `None` if the graph is not a DAG.
    fn dag_longest_path_weighted(&self) -> Option<(f64, Vec<u64>)>;

    /// Transitive closure: all `(src, dst)` pairs where `dst` is reachable from `src`.
    fn transitive_closure(&self) -> HashSet<(u64, u64)>;

    // ── Network Flow ───────────────────────────────────────────────────────

    /// Edmonds-Karp maximum flow from `source` to `sink`.
    ///
    /// Returns `Some((max_flow, edge_flows))` or `None` if source/sink not found.
    fn max_flow(&self, source: u64, sink: u64) -> MaxFlowResult;

    /// Minimum cut capacity between `source` and `sink`.
    fn min_cut_capacity(&self, source: u64, sink: u64) -> Option<f64>;

    // ── Coloring ──────────────────────────────────────────────────────────

    /// Greedy vertex coloring.
    ///
    /// Returns `HashMap<node_id, color>` (colors are 0-indexed integers).
    fn node_coloring(&self) -> HashMap<u64, usize>;

    /// Greedy edge coloring.
    ///
    /// Returns `HashMap<(src, dst), color>`.
    fn edge_coloring(&self) -> HashMap<(u64, u64), usize>;

    /// Approximate chromatic number (minimum colors needed).
    fn chromatic_number(&self) -> usize;

    // ── Matching ──────────────────────────────────────────────────────────

    /// Maximum weight matching edges.
    fn max_weight_matching_edges(&self) -> HashSet<(u64, u64)>;

    /// Maximum cardinality matching edges.
    fn max_cardinality_matching_edges(&self) -> HashSet<(u64, u64)>;

    // ── Community Detection ────────────────────────────────────────────────

    /// Louvain modularity-maximizing community detection.
    fn louvain_communities(&self, resolution: Option<f64>) -> CommunityInfo;

    /// Label propagation community detection.
    fn label_propagation_communities(&self) -> CommunityInfo;

    /// Girvan-Newman community detection (edge betweenness based).
    ///
    /// `k` is the target number of communities.
    fn girvan_newman_communities(&self, k: usize) -> CommunityInfo;

    // ── Components ────────────────────────────────────────────────────────

    /// Weakly connected components (ignoring edge direction).
    ///
    /// Returns a `Vec` of components, each component is a `Vec` of node IDs.
    fn connected_components_list(&self) -> Vec<Vec<u64>>;

    /// Strongly connected components (Tarjan's algorithm).
    ///
    /// Returns a `Vec` of SCCs, each SCC is a `Vec` of node IDs.
    fn strongly_connected_components_list(&self) -> Vec<Vec<u64>>;

    // ── Eulerian ──────────────────────────────────────────────────────────

    /// Returns `true` if the graph has an Euler circuit (all nodes even degree, connected).
    fn has_euler_circuit(&self) -> bool;

    /// Returns `true` if the graph has an Euler path (0 or 2 odd-degree nodes, connected).
    fn has_euler_path(&self) -> bool;

    /// Find an Euler circuit (sequence of node IDs, first == last).
    ///
    /// Returns `None` if no Euler circuit exists.
    fn euler_circuit(&self) -> Option<Vec<u64>>;

    /// Find an Euler path (sequence of node IDs).
    ///
    /// Returns `None` if no Euler path exists.
    fn euler_path(&self) -> Option<Vec<u64>>;

    // ── Planarity ─────────────────────────────────────────────────────────

    /// Returns `true` if the graph is planar (Boyer-Myrvold / LR planarity test).
    fn is_planar(&self) -> bool;
}
