use rust_apt::{Cache, Package, PackageSort, new_cache};
use std::collections::HashMap;
use std::path::PathBuf;
use tracing::{info, error};
use regex::Regex;

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

/// APT package manager wrapper
pub struct AptManager {
    cache: Cache,
}

impl AptManager {
    /// Create a new APT manager instance
    pub fn new() -> AptOstreeResult<Self> {
        info!("Initializing APT cache");
        
        // Add more robust error handling for FFI initialization
        let cache = match new_cache!() {
            Ok(cache) => {
                info!("APT cache initialized successfully");
                cache
            },
            Err(e) => {
                error!("Failed to initialize APT cache: {}", e);
                return Err(AptOstreeError::Apt(format!("Failed to initialize APT cache: {}", e)));
            }
        };
        
        Ok(Self { cache })
    }
    
    /// Get package information
    pub fn get_package(&self, name: &str) -> AptOstreeResult<Option<Package>> {
        Ok(self.cache.get(name))
    }
    
    /// List all packages
    pub fn list_packages(&self) -> impl Iterator<Item = Package> {
        self.cache.packages(&PackageSort::default())
    }
    
    /// List installed packages
    pub fn list_installed_packages(&self) -> impl Iterator<Item = Package> {
        self.cache.packages(&PackageSort::default()).filter(|pkg| pkg.is_installed())
    }
    
    /// List upgradable packages
    pub fn list_upgradable_packages(&self) -> impl Iterator<Item = Package> {
        // Placeholder: just return installed packages for now
        self.cache.packages(&PackageSort::default()).filter(|pkg| pkg.is_installed())
    }
    
    /// Search for packages
    pub fn search_packages_sync(&self, query: &str) -> Vec<Package> {
        // Return Vec to avoid lifetime issues
        self.cache.packages(&PackageSort::default())
            .filter(|pkg| pkg.name().contains(query))
            .collect()
    }
    
    /// Search for packages (async version for compatibility)
    pub async fn search_packages(&self, query: &str) -> AptOstreeResult<Vec<String>> {
        let packages = self.search_packages_sync(query);
        Ok(packages.into_iter().map(|pkg| pkg.name().to_string()).collect())
    }
    
    /// Enhanced search for packages with advanced options
    pub async fn search_packages_enhanced(&self, query: &str, opts: &SearchOpts) -> AptOstreeResult<Vec<SearchResult>> {
        // 1. Prepare search query
        let search_query = if opts.ignore_case {
            query.to_lowercase()
        } else {
            query.to_string()
        };
        
        // 2. Compile regex pattern for flexible matching
        let pattern = if opts.ignore_case {
            Regex::new(&format!("(?i){}", regex::escape(&search_query)))
                .map_err(|e| AptOstreeError::InvalidArgument(format!("Invalid search pattern: {}", e)))?
        } else {
            Regex::new(&regex::escape(&search_query))
                .map_err(|e| AptOstreeError::InvalidArgument(format!("Invalid search pattern: {}", e)))?
        };
        
        // 3. Get all packages from cache
        let packages = self.cache.packages(&PackageSort::default());
        
        // 4. Search and filter packages
        let mut results = Vec::new();
        
        for package in packages {
            // Check if package matches search criteria
            if self.matches_search_criteria(&package, &pattern, &search_query, opts).await? {
                let result = self.create_search_result(&package, opts).await?;
                results.push(result);
            }
        }
        
        // 5. Sort results by relevance
        results.sort_by(|a, b| {
            // Sort by exact name matches first, then by relevance score
            let a_exact = a.name.to_lowercase() == search_query;
            let b_exact = b.name.to_lowercase() == search_query;
            
            match (a_exact, b_exact) {
                (true, false) => std::cmp::Ordering::Less,
                (false, true) => std::cmp::Ordering::Greater,
                _ => b.relevance_score.cmp(&a.relevance_score),
            }
        });
        
        // 6. Apply limit if specified
        if let Some(limit) = opts.limit {
            results.truncate(limit);
        }
        
        Ok(results)
    }
    
    /// Check if a package matches the search criteria
    async fn matches_search_criteria(&self, package: &Package<'_>, pattern: &Regex, search_query: &str, opts: &SearchOpts) -> AptOstreeResult<bool> {
        let name = package.name().to_lowercase();
        
        // Check installed/available filters
        if opts.installed_only && !package.is_installed() {
            return Ok(false);
        }
        
        if opts.available_only && package.is_installed() {
            return Ok(false);
        }
        
        // Check name matching
        if pattern.is_match(&name) {
            return Ok(true);
        }
        
        // For now, only search by name since description methods are not available
        // TODO: Add description search when rust-apt exposes these methods
        Ok(false)
    }
    
    /// Create a search result from a package
    async fn create_search_result(&self, package: &Package<'_>, opts: &SearchOpts) -> AptOstreeResult<SearchResult> {
        let name = package.name().to_string();
        let search_query = if opts.ignore_case {
            opts.query.to_lowercase()
        } else {
            opts.query.clone()
        };
        
        // Get version information
        let version = {
            let version_info = unsafe { package.current_version() };
            if version_info.is_null() {
                "unknown".to_string()
            } else {
                unsafe {
                    match version_info.as_ref() {
                        Some(ver) => ver.version().to_string(),
                        None => "unknown".to_string(),
                    }
                }
            }
        };
        
        // Get installed version if different
        let installed_version = if package.is_installed() {
            let installed_ver = package.install_version();
            if let Some(ver) = installed_ver {
                let inst_ver = ver.version().to_string();
                if inst_ver != version {
                    Some(inst_ver)
                } else {
                    None
                }
            } else {
                None
            }
        } else {
            None
        };
        
        // Get description (placeholder for now)
        let description = if opts.name_only {
            "".to_string()
        } else {
            "No description available".to_string()
        };
        
        // Get architecture (placeholder for now)
        let architecture = "unknown".to_string();
        
        // Calculate size (placeholder for now)
        let size = 0;
        
        // Calculate relevance score
        let relevance_score = self.calculate_relevance_score(package, &search_query, opts).await?;
        
        // Check if installed
        let is_installed = package.is_installed();
        
        Ok(SearchResult {
            name,
            version,
            description,
            architecture,
            installed_version,
            size,
            relevance_score,
            is_installed,
        })
    }
    
    /// Calculate relevance score for search results
    async fn calculate_relevance_score(&self, package: &Package<'_>, search_query: &str, opts: &SearchOpts) -> AptOstreeResult<u32> {
        let mut score = 0;
        let name = package.name().to_lowercase();
        
        // Exact name match gets highest score
        if name == *search_query {
            score += 1000;
        }
        
        // Name starts with query
        if name.starts_with(search_query) {
            score += 500;
        }
        
        // Name contains query
        if name.contains(search_query) {
            score += 100;
        }
        
        // Description contains query (if not name-only)
        // TODO: Add description scoring when rust-apt exposes description methods
        if !opts.name_only {
            // For now, no description scoring
        }
        
        // Long description contains query (if verbose)
        // TODO: Add long description scoring when rust-apt exposes description methods
        if opts.verbose && !opts.name_only {
            // For now, no long description scoring
        }
        
        // Installed packages get slight bonus
        if package.is_installed() {
            score += 10;
        }
        
        Ok(score)
    }
    
    /// Resolve package dependencies
    pub fn resolve_dependencies(&self, package_names: &[String]) -> AptOstreeResult<Vec<Package>> {
        let mut resolved_packages = Vec::new();
        let mut visited = std::collections::HashSet::new();
        for name in package_names {
            if let Some(pkg) = self.get_package(name)? {
                if !visited.contains(pkg.name()) {
                    visited.insert(pkg.name().to_string());
                    resolved_packages.push(pkg);
                }
            } else {
                return Err(AptOstreeError::PackageNotFound(name.clone()));
            }
        }
        Ok(resolved_packages)
    }
    
    /// Check for dependency conflicts
    pub fn check_conflicts(&self, _packages: &[Package]) -> AptOstreeResult<Vec<String>> {
        // Placeholder: no real conflict checking
        Ok(vec![])
    }
    
    /// Get package metadata
    pub fn get_package_metadata(&self, package: &Package) -> AptOstreeResult<PackageMetadata> {
        // Only use available methods: name and version
        let name = package.name().to_string();
        
        // Safer version handling with proper null checks
        let version = {
            let version_info = unsafe { package.current_version() };
            if version_info.is_null() {
                String::new()
            } else {
                unsafe {
                    match version_info.as_ref() {
                        Some(ver) => ver.version().to_string(),
                        None => String::new(),
                    }
                }
            }
        };
        
        // TODO: When rust-apt exposes these fields, extract them here
        let architecture = String::new();
        let description = String::new();
        let section = String::new();
        let priority = String::new();
        Ok(PackageMetadata {
            name,
            version,
            architecture,
            description,
            section,
            priority,
            depends: HashMap::new(),
            conflicts: HashMap::new(),
            provides: HashMap::new(),
        })
    }
    
    /// Get package metadata by name (async version for compatibility)
    pub async fn get_package_metadata_by_name(&self, package_name: &str) -> AptOstreeResult<DebPackageMetadata> {
        if let Some(package) = self.get_package(package_name)? {
            let metadata = self.get_package_metadata(&package)?;
            Ok(DebPackageMetadata {
                name: metadata.name,
                version: metadata.version,
                architecture: metadata.architecture,
                description: metadata.description,
                depends: vec![],
                conflicts: vec![],
                provides: vec![],
                scripts: HashMap::new(), // TODO: Extract scripts from package
            })
        } else {
            Err(AptOstreeError::PackageNotFound(package_name.to_string()))
        }
    }
    
    /// Get package info (alias for get_package_metadata)
    pub async fn get_package_info(&self, package_name: &str) -> AptOstreeResult<DebPackageMetadata> {
        self.get_package_metadata_by_name(package_name).await
    }
    
    /// Download package
    pub async fn download_package(&self, package_name: &str) -> AptOstreeResult<PathBuf> {
        info!("Downloading package: {}", package_name);
        
        // Get the package from cache
        let package = self.get_package(package_name)?
            .ok_or_else(|| AptOstreeError::PackageNotFound(package_name.to_string()))?;
        
        // Get the current version (candidate for installation)
        let version_info = package.candidate();
        if version_info.is_none() {
            return Err(AptOstreeError::PackageNotFound(format!("No candidate version for {}", package_name)));
        }
        
        let version = version_info.unwrap().version().to_string();
        
        // Construct the expected package filename
        let architecture = "amd64".to_string(); // TODO: Get from package metadata
        
        let package_filename = if architecture == "all" {
            format!("{}_{}_{}.deb", package_name, version, architecture)
        } else {
            format!("{}_{}_{}.deb", package_name, version, architecture)
        };
        
        // Check if package is already in cache
        let cache_dir = "/var/cache/apt/archives";
        let package_path = PathBuf::from(format!("{}/{}", cache_dir, package_filename));
        
        if package_path.exists() {
            info!("Package already in cache: {:?}", package_path);
            return Ok(package_path);
        }
        
        // Use apt-get to download the package
        info!("Would download package to: {:?}", package_path);
        
        let output = std::process::Command::new("apt-get")
            .args(&["download", package_name])
            .current_dir(cache_dir)
            .output()
            .map_err(|e| AptOstreeError::Io(e))?;
        
        if !output.status.success() {
            let error_msg = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::PackageNotFound(
                format!("Failed to download {}: {}", package_name, error_msg)
            ));
        }
        
        // Verify the downloaded file exists and has content
        if !package_path.exists() {
            return Err(AptOstreeError::PackageNotFound(
                format!("Downloaded package file not found: {:?}", package_path)
            ));
        }
        
        let metadata = std::fs::metadata(&package_path)
            .map_err(|e| AptOstreeError::Io(e))?;
        
        if metadata.len() == 0 {
            return Err(AptOstreeError::PackageNotFound(
                format!("Downloaded package file is empty: {:?}", package_path)
            ));
        }
        
        info!("Downloaded package to: {:?}", package_path);
        Ok(package_path)
    }
    
    /// Install package
    pub async fn install_package(&self, package_name: &str) -> AptOstreeResult<()> {
        // In a real implementation, this would:
        // 1. Download the package
        // 2. Extract it
        // 3. Install it to the filesystem
        
        info!("Installing package: {}", package_name);
        
        // Simulate package installation
        tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
        
        info!("Package {} installed successfully", package_name);
        Ok(())
    }

    /// Clear the APT cache
    pub async fn clear_cache(&self) -> AptOstreeResult<()> {
        info!("Clearing APT cache");
        
        // In a real implementation, this would:
        // 1. Clear /var/cache/apt/archives/
        // 2. Clear /var/lib/apt/lists/
        // 3. Clear package lists
        // 4. Reset APT cache
        
        // Simulate cache clearing
        tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
        
        info!("APT cache cleared successfully");
        Ok(())
    }
    
    /// Remove package
    pub async fn remove_package(&self, package_name: &str) -> AptOstreeResult<()> {
        // Placeholder: just log the removal
        info!("Would remove package: {}", package_name);
        // TODO: Implement actual package removal
        Ok(())
    }
    
    /// Upgrade package
    pub async fn upgrade_package(&self, package_name: &str) -> AptOstreeResult<()> {
        // Placeholder: just log the upgrade
        info!("Would upgrade package: {}", package_name);
        // TODO: Implement actual package upgrade
        Ok(())
    }
    
    /// Get upgradable packages
    pub async fn get_upgradable_packages(&self) -> AptOstreeResult<Vec<String>> {
        // Placeholder: return empty list
        // TODO: Implement actual upgradable package detection
        Ok(vec![])
    }
    
    /// Get package dependencies
    pub fn get_package_dependencies(&self, _package: &Package) -> AptOstreeResult<Vec<String>> {
        // Placeholder: return empty dependencies for now
        // TODO: Implement actual dependency resolution
        Ok(vec![])
    }
    
    /// Get reverse dependencies (packages that depend on this package)
    pub fn get_reverse_dependencies(&self, _package_name: &str) -> AptOstreeResult<Vec<String>> {
        // Placeholder: return empty reverse dependencies for now
        // TODO: Implement actual reverse dependency resolution
        Ok(vec![])
    }
}

/// Package metadata structure
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PackageMetadata {
    pub name: String,
    pub version: String,
    pub architecture: String,
    pub description: String,
    pub section: String,
    pub priority: String,
    pub depends: HashMap<String, usize>,
    pub conflicts: HashMap<String, usize>,
    pub provides: HashMap<String, usize>,
} 