//! Extension DAG (Directed Acyclic Graph) operations for dependency resolution

use std::collections::HashMap;

use petgraph::graph::{DiGraph, NodeIndex};
use serde::{Deserialize, Serialize};

/// Extension dependency graph
pub struct ExtensionDag {
    graph: DiGraph<Extension, String>,
    nodes: HashMap<String, NodeIndex>,
}

/// Extension metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Extension {
    pub name: String,
    pub version: String,
    pub description: String,
}

impl ExtensionDag {
    /// Create a new extension DAG
    pub fn new() -> Self {
        Self {
            graph: DiGraph::new(),
            nodes: HashMap::new(),
        }
    }

    /// Add an extension to the DAG
    pub fn add_extension(&mut self, name: String, version: String, description: String) {
        let extension = Extension {
            name: name.clone(),
            version,
            description,
        };
        let idx = self.graph.add_node(extension);
        self.nodes.insert(name, idx);
    }

    /// Add a dependency between extensions
    pub fn add_dependency(&mut self, from: &str, to: &str) -> Result<(), String> {
        let from_idx = self
            .nodes
            .get(from)
            .ok_or_else(|| format!("Extension not found: {}", from))?;
        let to_idx = self
            .nodes
            .get(to)
            .ok_or_else(|| format!("Extension not found: {}", to))?;

        self.graph
            .add_edge(*from_idx, *to_idx, format!("{} depends on {}", from, to));
        Ok(())
    }

    /// Get topological sort (initialization order)
    pub fn topological_sort(&self) -> Result<Vec<String>, String> {
        match petgraph::algo::toposort(&self.graph, None) {
            Ok(order) => Ok(order
                .iter()
                .map(|idx| self.graph[*idx].name.clone())
                .collect()),
            Err(_) => Err("Circular dependency detected".to_string()),
        }
    }

    /// Get dependencies for an extension
    pub fn get_dependencies(&self, name: &str) -> Result<Vec<String>, String> {
        let idx = self
            .nodes
            .get(name)
            .ok_or_else(|| format!("Extension not found: {}", name))?;

        let deps = self
            .graph
            .neighbors(*idx)
            .map(|neighbor_idx| self.graph[neighbor_idx].name.clone())
            .collect();

        Ok(deps)
    }

    /// Export DAG as nodes and edges
    pub fn export(&self) -> (Vec<Extension>, Vec<(String, String)>) {
        let nodes: Vec<Extension> = self.graph.node_weights().cloned().collect();

        let edges: Vec<(String, String)> = self
            .graph
            .raw_edges()
            .iter()
            .map(|edge| {
                let from = self.graph[edge.source()].name.clone();
                let to = self.graph[edge.target()].name.clone();
                (from, to)
            })
            .collect();

        (nodes, edges)
    }
}

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