//! Package Dependency Resolver for APT-OSTree
//! 
//! This module implements dependency resolution for DEB packages in the context
//! of OSTree commits, ensuring proper layering order and conflict resolution.

use std::collections::{HashMap, HashSet, VecDeque};
use tracing::{info, warn, debug};
use serde::{Serialize, Deserialize};

use crate::error::{AptOstreeError, AptOstreeResult};
use crate::apt_ostree_integration::DebPackageMetadata;

/// Dependency relationship types
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum DependencyRelation {
    Depends,
    Recommends,
    Suggests,
    Conflicts,
    Breaks,
    Provides,
    Replaces,
}

/// Dependency constraint
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyConstraint {
    pub package_name: String,
    pub version_constraint: Option<VersionConstraint>,
    pub relation: DependencyRelation,
}

/// Version constraint
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VersionConstraint {
    pub operator: VersionOperator,
    pub version: String,
}

/// Version comparison operators
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum VersionOperator {
    LessThan,
    LessThanOrEqual,
    Equal,
    GreaterThanOrEqual,
    GreaterThan,
    NotEqual,
}

/// Resolved dependency graph
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyGraph {
    pub nodes: HashMap<String, PackageNode>,
    pub edges: Vec<DependencyEdge>,
}

/// Package node in dependency graph
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackageNode {
    pub name: String,
    pub metadata: DebPackageMetadata,
    pub dependencies: Vec<DependencyConstraint>,
    pub level: usize,
    pub visited: bool,
}

/// Dependency edge in graph
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyEdge {
    pub from: String,
    pub to: String,
    pub relation: DependencyRelation,
}

/// Dependency resolver for OSTree packages
pub struct DependencyResolver {
    available_packages: HashMap<String, DebPackageMetadata>,
}

impl DependencyResolver {
    /// Create a new dependency resolver
    pub fn new() -> Self {
        Self {
            available_packages: HashMap::new(),
        }
    }
    
    /// Add available packages to the resolver
    pub fn add_available_packages(&mut self, packages: Vec<DebPackageMetadata>) {
        for package in packages {
            self.available_packages.insert(package.name.clone(), package);
        }
        info!("Added {} available packages to resolver", self.available_packages.len());
    }
    
    /// Resolve dependencies for a list of packages
    pub fn resolve_dependencies(&self, package_names: &[String]) -> AptOstreeResult<ResolvedDependencies> {
        info!("Resolving dependencies for {} packages", package_names.len());
        
        // Build dependency graph
        let graph = self.build_dependency_graph(package_names)?;
        
        // Check for conflicts
        let conflicts = self.check_conflicts(&graph)?;
        if !conflicts.is_empty() {
            return Err(AptOstreeError::DependencyConflict(
                format!("Dependency conflicts found: {:?}", conflicts)
            ));
        }
        
        // Topological sort for layering order
        let layering_order = self.topological_sort(&graph)?;
        
        // Calculate dependency levels
        let leveled_packages = self.calculate_dependency_levels(&graph, &layering_order)?;
        
        Ok(ResolvedDependencies {
            packages: layering_order,
            levels: leveled_packages,
            graph,
        })
    }
    
    /// Build dependency graph from package names
    fn build_dependency_graph(&self, package_names: &[String]) -> AptOstreeResult<DependencyGraph> {
        let mut graph = DependencyGraph {
            nodes: HashMap::new(),
            edges: Vec::new(),
        };
        
        // Add requested packages
        for package_name in package_names {
            if let Some(metadata) = self.available_packages.get(package_name) {
                let node = PackageNode {
                    name: package_name.clone(),
                    metadata: metadata.clone(),
                    dependencies: self.parse_dependencies(&metadata.depends),
                    level: 0,
                    visited: false,
                };
                graph.nodes.insert(package_name.clone(), node);
            } else {
                return Err(AptOstreeError::PackageNotFound(package_name.clone()));
            }
        }
        
        // Add dependencies recursively
        let mut to_process: VecDeque<String> = package_names.iter().cloned().collect();
        let mut processed = HashSet::new();
        
        while let Some(package_name) = to_process.pop_front() {
            if processed.contains(&package_name) {
                continue;
            }
            processed.insert(package_name.clone());
            
            if let Some(node) = graph.nodes.get(&package_name) {
                // Collect dependencies to avoid borrow checker issues
                let dependencies = node.dependencies.clone();
                
                for dep_constraint in &dependencies {
                    let dep_name = &dep_constraint.package_name;
                    
                    // Add dependency node if not already present
                    if !graph.nodes.contains_key(dep_name) {
                        if let Some(dep_metadata) = self.available_packages.get(dep_name) {
                            let dep_node = PackageNode {
                                name: dep_name.clone(),
                                metadata: dep_metadata.clone(),
                                dependencies: self.parse_dependencies(&dep_metadata.depends),
                                level: 0,
                                visited: false,
                            };
                            graph.nodes.insert(dep_name.clone(), dep_node);
                            to_process.push_back(dep_name.clone());
                        } else {
                            warn!("Dependency not found: {}", dep_name);
                        }
                    }
                    
                    // Add edge
                    graph.edges.push(DependencyEdge {
                        from: package_name.clone(),
                        to: dep_name.clone(),
                        relation: dep_constraint.relation.clone(),
                    });
                }
            }
        }
        
        info!("Built dependency graph with {} nodes and {} edges", graph.nodes.len(), graph.edges.len());
        Ok(graph)
    }
    
    /// Parse dependency strings into structured constraints
    fn parse_dependencies(&self, deps_str: &[String]) -> Vec<DependencyConstraint> {
        let mut constraints = Vec::new();
        
        for dep_str in deps_str {
            // Simple parsing - in real implementation, this would be more sophisticated
            let parts: Vec<&str> = dep_str.split_whitespace().collect();
            if !parts.is_empty() {
                let package_name = parts[0].to_string();
                let version_constraint = if parts.len() > 1 {
                    self.parse_version_constraint(&parts[1..])
                } else {
                    None
                };
                
                constraints.push(DependencyConstraint {
                    package_name,
                    version_constraint,
                    relation: DependencyRelation::Depends,
                });
            }
        }
        
        constraints
    }
    
    /// Parse version constraint from string parts
    fn parse_version_constraint(&self, parts: &[&str]) -> Option<VersionConstraint> {
        if parts.is_empty() {
            return None;
        }
        
        let constraint_str = parts.join(" ");
        
        // Simple version constraint parsing
        // In real implementation, this would handle complex Debian version constraints
        if constraint_str.starts_with(">=") {
            Some(VersionConstraint {
                operator: VersionOperator::GreaterThanOrEqual,
                version: constraint_str[2..].trim().to_string(),
            })
        } else if constraint_str.starts_with("<=") {
            Some(VersionConstraint {
                operator: VersionOperator::LessThanOrEqual,
                version: constraint_str[2..].trim().to_string(),
            })
        } else if constraint_str.starts_with(">") {
            Some(VersionConstraint {
                operator: VersionOperator::GreaterThan,
                version: constraint_str[1..].trim().to_string(),
            })
        } else if constraint_str.starts_with("<") {
            Some(VersionConstraint {
                operator: VersionOperator::LessThan,
                version: constraint_str[1..].trim().to_string(),
            })
        } else if constraint_str.starts_with("=") {
            Some(VersionConstraint {
                operator: VersionOperator::Equal,
                version: constraint_str[1..].trim().to_string(),
            })
        } else {
            // Assume exact version match
            Some(VersionConstraint {
                operator: VersionOperator::Equal,
                version: constraint_str.to_string(),
            })
        }
    }
    
    /// Check for dependency conflicts
    fn check_conflicts(&self, graph: &DependencyGraph) -> AptOstreeResult<Vec<String>> {
        let mut conflicts = Vec::new();
        
        // Check for direct conflicts
        for node in graph.nodes.values() {
            for conflict in &node.metadata.conflicts {
                if graph.nodes.contains_key(conflict) {
                    conflicts.push(format!("{} conflicts with {}", node.name, conflict));
                }
            }
        }
        
        // Check for circular dependencies
        if self.has_circular_dependencies(graph)? {
            conflicts.push("Circular dependency detected".to_string());
        }
        
        if !conflicts.is_empty() {
            warn!("Found {} conflicts", conflicts.len());
        }
        
        Ok(conflicts)
    }
    
    /// Check for circular dependencies using DFS
    fn has_circular_dependencies(&self, graph: &DependencyGraph) -> AptOstreeResult<bool> {
        let mut visited = HashSet::new();
        let mut rec_stack = HashSet::new();
        
        for node_name in graph.nodes.keys() {
            if !visited.contains(node_name) {
                if self.is_cyclic_util(graph, node_name, &mut visited, &mut rec_stack)? {
                    return Ok(true);
                }
            }
        }
        
        Ok(false)
    }
    
    /// Utility function for cycle detection
    fn is_cyclic_util(
        &self,
        graph: &DependencyGraph,
        node_name: &str,
        visited: &mut HashSet<String>,
        rec_stack: &mut HashSet<String>,
    ) -> AptOstreeResult<bool> {
        visited.insert(node_name.to_string());
        rec_stack.insert(node_name.to_string());
        
        for edge in &graph.edges {
            if edge.from == *node_name {
                let neighbor = &edge.to;
                
                if !visited.contains(neighbor) {
                    if self.is_cyclic_util(graph, neighbor, visited, rec_stack)? {
                        return Ok(true);
                    }
                } else if rec_stack.contains(neighbor) {
                    return Ok(true);
                }
            }
        }
        
        rec_stack.remove(node_name);
        Ok(false)
    }
    
    /// Perform topological sort for layering order
    fn topological_sort(&self, graph: &DependencyGraph) -> AptOstreeResult<Vec<String>> {
        let mut in_degree: HashMap<String, usize> = HashMap::new();
        let mut queue: VecDeque<String> = VecDeque::new();
        let mut result = Vec::new();
        
        // Initialize in-degrees
        for node_name in graph.nodes.keys() {
            in_degree.insert(node_name.clone(), 0);
        }
        
        // Calculate in-degrees
        for edge in &graph.edges {
            *in_degree.get_mut(&edge.to).unwrap() += 1;
        }
        
        // Add nodes with no dependencies to queue
        for (node_name, degree) in &in_degree {
            if *degree == 0 {
                queue.push_back(node_name.clone());
            }
        }
        
        // Process queue
        while let Some(node_name) = queue.pop_front() {
            result.push(node_name.clone());
            
            // Reduce in-degree of neighbors
            for edge in &graph.edges {
                if edge.from == *node_name {
                    let neighbor = &edge.to;
                    if let Some(degree) = in_degree.get_mut(neighbor) {
                        *degree -= 1;
                        if *degree == 0 {
                            queue.push_back(neighbor.clone());
                        }
                    }
                }
            }
        }
        
        // Check if all nodes were processed
        if result.len() != graph.nodes.len() {
            return Err(AptOstreeError::DependencyConflict(
                "Circular dependency detected during topological sort".to_string()
            ));
        }
        
        info!("Topological sort completed: {:?}", result);
        Ok(result)
    }
    
    /// Calculate dependency levels for layering
    fn calculate_dependency_levels(
        &self,
        graph: &DependencyGraph,
        layering_order: &[String],
    ) -> AptOstreeResult<Vec<Vec<String>>> {
        let mut levels: Vec<Vec<String>> = Vec::new();
        let mut node_levels: HashMap<String, usize> = HashMap::new();
        
        for node_name in layering_order {
            let mut max_dep_level = 0;
            
            // Find maximum level of dependencies
            for edge in &graph.edges {
                if edge.from == *node_name {
                    if let Some(dep_level) = node_levels.get(&edge.to) {
                        max_dep_level = max_dep_level.max(*dep_level + 1);
                    }
                }
            }
            
            node_levels.insert(node_name.clone(), max_dep_level);
            
            // Add to appropriate level
            while levels.len() <= max_dep_level {
                levels.push(Vec::new());
            }
            levels[max_dep_level].push(node_name.clone());
        }
        
        info!("Calculated {} dependency levels", levels.len());
        for (i, level) in levels.iter().enumerate() {
            debug!("Level {}: {:?}", i, level);
        }
        
        Ok(levels)
    }
}

/// Resolved dependencies result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedDependencies {
    pub packages: Vec<String>,
    pub levels: Vec<Vec<String>>,
    pub graph: DependencyGraph,
}

impl ResolvedDependencies {
    /// Get packages in layering order
    pub fn layering_order(&self) -> &[String] {
        &self.packages
    }
    
    /// Get packages grouped by dependency level
    pub fn by_level(&self) -> &[Vec<String>] {
        &self.levels
    }
    
    /// Get total number of packages
    pub fn package_count(&self) -> usize {
        self.packages.len()
    }
    
    /// Get number of dependency levels
    pub fn level_count(&self) -> usize {
        self.levels.len()
    }
} 