//! Feature dependency resolver

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

use anyhow::{anyhow, Result};

use super::feature::FeatureManager;

#[derive(Debug, Clone)]
pub struct DependencyGraph {
    pub nodes: HashMap<String, FeatureNode>,
    pub edges: HashMap<String, Vec<String>>,
}

#[derive(Debug, Clone)]
pub struct FeatureNode {
    #[allow(dead_code)]
    pub name: String,
    #[allow(dead_code)]
    pub version: String,
    #[allow(dead_code)]
    pub installed: bool,
    #[allow(dead_code)]
    pub requires: Vec<String>,
}

pub struct DependencyResolver {
    manager: FeatureManager,
}

impl DependencyResolver {
    pub fn new(manager: FeatureManager) -> Self {
        Self { manager }
    }

    pub fn resolve_dependencies(&self, feature_name: &str) -> Result<Vec<String>> {
        println!("🔍 Resolving dependencies for '{}'...", feature_name);

        // Build dependency graph
        let graph = self.build_dependency_graph(feature_name)?;

        // Check for circular dependencies
        self.check_circular_dependencies(&graph)?;

        // Topological sort for installation order
        let install_order = self.topological_sort(&graph)?;

        println!("  ✓ Dependencies resolved: {:?}", install_order);
        Ok(install_order)
    }

    fn build_dependency_graph(&self, root_feature: &str) -> Result<DependencyGraph> {
        let mut graph = DependencyGraph {
            nodes: HashMap::new(),
            edges: HashMap::new(),
        };

        let mut to_process = VecDeque::new();
        let mut processed = HashSet::new();

        to_process.push_back(root_feature.to_string());

        while let Some(current_feature) = to_process.pop_front() {
            if processed.contains(&current_feature) {
                continue;
            }

            processed.insert(current_feature.clone());

            // Load feature manifest
            let manifest = self.manager.load_feature_manifest(&current_feature)?;

            let requires = manifest.feature.requires.unwrap_or_default();

            // Add node to graph
            graph.nodes.insert(
                current_feature.clone(),
                FeatureNode {
                    name: current_feature.clone(),
                    version: manifest.feature.version,
                    installed: false, // Would check if actually installed
                    requires: requires.clone(),
                },
            );

            // Add edges
            graph
                .edges
                .insert(current_feature.clone(), requires.clone());

            // Add dependencies to process queue
            for dep in requires {
                if !processed.contains(&dep) {
                    to_process.push_back(dep);
                }
            }
        }

        Ok(graph)
    }

    fn check_circular_dependencies(&self, graph: &DependencyGraph) -> Result<()> {
        // Implement cycle detection algorithm (DFS-based)
        let mut white = HashSet::new();
        let mut gray = HashSet::new();
        let mut black = HashSet::new();

        // Initialize all nodes as white (unvisited)
        for node_name in graph.nodes.keys() {
            white.insert(node_name.clone());
        }

        // Check each node
        for node_name in graph.nodes.keys() {
            if white.contains(node_name)
                && self.has_cycle_dfs(node_name, graph, &mut white, &mut gray, &mut black)?
            {
                return Err(anyhow!(
                    "Circular dependency detected involving '{}'.",
                    node_name
                ));
            }
        }

        Ok(())
    }

    fn has_cycle_dfs(
        &self,
        node: &str,
        graph: &DependencyGraph,
        white: &mut HashSet<String>,
        gray: &mut HashSet<String>,
        black: &mut HashSet<String>,
    ) -> Result<bool> {
        // Move node from white to gray
        white.remove(node);
        gray.insert(node.to_string());

        // Check all dependencies
        if let Some(dependencies) = graph.edges.get(node) {
            for dep in dependencies {
                if gray.contains(dep) {
                    // Back edge found - cycle detected
                    return Ok(true);
                }

                if white.contains(dep) && self.has_cycle_dfs(dep, graph, white, gray, black)? {
                    return Ok(true);
                }
            }
        }

        // Move node from gray to black
        gray.remove(node);
        black.insert(node.to_string());

        Ok(false)
    }

    fn topological_sort(&self, graph: &DependencyGraph) -> Result<Vec<String>> {
        let mut in_degree = HashMap::new();
        let mut result = Vec::new();
        let mut queue = VecDeque::new();

        // Initialize in-degree count
        for node_name in graph.nodes.keys() {
            in_degree.insert(node_name.clone(), 0);
        }

        // Calculate in-degrees
        for dependencies in graph.edges.values() {
            for dep in dependencies {
                *in_degree.entry(dep.clone()).or_insert(0) += 1;
            }
        }

        // Find nodes with no incoming edges
        for (node, degree) in &in_degree {
            if *degree == 0 {
                queue.push_back(node.clone());
            }
        }

        // Process queue
        while let Some(current) = queue.pop_front() {
            result.push(current.clone());

            // Reduce in-degree for all dependents
            for (node, dependencies) in &graph.edges {
                if dependencies.contains(&current) {
                    let degree = in_degree.get_mut(node).unwrap();
                    *degree -= 1;
                    if *degree == 0 {
                        queue.push_back(node.clone());
                    }
                }
            }
        }

        // Check if all nodes are processed (no cycles)
        if result.len() != graph.nodes.len() {
            return Err(anyhow!(
                "Dependency cycle detected - cannot resolve installation order"
            ));
        }

        Ok(result)
    }

    #[allow(dead_code)]
    pub fn check_conflicts(&self, _features: &[String]) -> Result<Vec<String>> {
        let conflicts = Vec::new();

        // Check for conflicting features
        // This would involve loading feature manifests and checking for conflicts

        Ok(conflicts)
    }
}
