//! APT package management for apt-ostree daemon

use crate::daemon::{DaemonResult, DaemonError};
use std::process::Command;
use std::path::Path;

use serde::{Serialize, Deserialize};

/// Package information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackageInfo {
    pub name: String,
    pub version: String,
    pub description: String,
    pub depends: Vec<String>,
    pub conflicts: Vec<String>,
    pub provides: Vec<String>,
    pub size: u64,
    pub priority: String,
    pub section: String,
}

/// APT manager for the daemon
#[allow(dead_code)]
pub struct AptManager {
    cache_dir: String,
    cache_updated: bool,
}

impl AptManager {
    pub fn new(cache_dir: &str) -> DaemonResult<Self> {
        // Ensure cache directory exists
        let cache_path = Path::new(cache_dir);
        if !cache_path.exists() {
            std::fs::create_dir_all(cache_path)
                .map_err(|e| DaemonError::System(format!("Failed to create cache directory: {}", e)))?;
        }

        Ok(Self {
            cache_dir: cache_dir.to_string(),
            cache_updated: false,
        })
    }

    pub fn update_cache(&mut self) -> DaemonResult<()> {
        tracing::info!("Updating APT cache");
        
        // Run apt-get update
        let output = Command::new("apt-get")
            .arg("update")
            .output()
            .map_err(|e| DaemonError::System(format!("Failed to run apt-get update: {}", e)))?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(DaemonError::System(format!("apt-get update failed: {}", error)));
        }

        self.cache_updated = true;
        tracing::info!("APT cache updated successfully");
        Ok(())
    }

    pub fn install_packages(&self, packages: &[String]) -> DaemonResult<()> {
        if !self.cache_updated {
            return Err(DaemonError::System("Cache not updated. Call update_cache() first.".to_string()));
        }

        tracing::info!("Installing packages: {:?}", packages);
        
        // Run apt-get install
        let mut cmd = Command::new("apt-get");
        cmd.arg("install");
        cmd.arg("-y"); // Non-interactive
        cmd.args(packages);

        let output = cmd.output()
            .map_err(|e| DaemonError::System(format!("Failed to run apt-get install: {}", e)))?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(DaemonError::System(format!("apt-get install failed: {}", error)));
        }

        tracing::info!("Packages installed successfully");
        Ok(())
    }

    pub fn remove_packages(&self, packages: &[String]) -> DaemonResult<()> {
        tracing::info!("Removing packages: {:?}", packages);
        
        // Run apt-get remove
        let mut cmd = Command::new("apt-get");
        cmd.arg("remove");
        cmd.arg("-y"); // Non-interactive
        cmd.args(packages);

        let output = cmd.output()
            .map_err(|e| DaemonError::System(format!("Failed to run apt-get remove: {}", e)))?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(DaemonError::System(format!("apt-get remove failed: {}", error)));
        }

        tracing::info!("Packages removed successfully");
        Ok(())
    }

    pub fn upgrade_system(&self) -> DaemonResult<()> {
        if !self.cache_updated {
            return Err(DaemonError::System("Cache not updated. Call update_cache() first.".to_string()));
        }

        tracing::info!("Upgrading system");
        
        // Run apt-get upgrade
        let output = Command::new("apt-get")
            .arg("upgrade")
            .arg("-y") // Non-interactive
            .output()
            .map_err(|e| DaemonError::System(format!("Failed to run apt-get upgrade: {}", e)))?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(DaemonError::System(format!("apt-get upgrade failed: {}", error)));
        }

        tracing::info!("System upgraded successfully");
        Ok(())
    }

    pub fn get_package_info(&self, package: &str) -> DaemonResult<PackageInfo> {
        if !self.cache_updated {
            return Err(DaemonError::System("Cache not updated. Call update_cache() first.".to_string()));
        }

        // Run apt-cache show to get package information
        let output = Command::new("apt-cache")
            .arg("show")
            .arg(package)
            .output()
            .map_err(|e| DaemonError::System(format!("Failed to run apt-cache show: {}", e)))?;

        if !output.status.success() {
            return Err(DaemonError::System(format!("Package {} not found", package)));
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        let package_info = self.parse_package_info(&output_str, package)?;
        
        Ok(package_info)
    }

    pub fn resolve_dependencies(&self, packages: &[String]) -> DaemonResult<Vec<String>> {
        if !self.cache_updated {
            return Err(DaemonError::System("Cache not updated. Call update_cache() first.".to_string()));
        }

        let mut all_deps = Vec::new();
        
        for package in packages {
            let deps = self.get_package_dependencies(package)?;
            all_deps.extend(deps);
        }

        // Remove duplicates and sort
        all_deps.sort();
        all_deps.dedup();
        
        Ok(all_deps)
    }

    pub fn search_packages(&self, query: &str) -> DaemonResult<Vec<PackageInfo>> {
        if !self.cache_updated {
            return Err(DaemonError::System("Cache not updated. Call update_cache() first.".to_string()));
        }

        // Run apt-cache search
        let output = Command::new("apt-cache")
            .arg("search")
            .arg(query)
            .output()
            .map_err(|e| DaemonError::System(format!("Failed to run apt-cache search: {}", e)))?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(DaemonError::System(format!("apt-cache search failed: {}", error)));
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        let packages = self.parse_search_results(&output_str)?;
        
        Ok(packages)
    }

    pub fn is_package_installed(&self, package: &str) -> DaemonResult<bool> {
        // Check if package is installed using dpkg
        let output = Command::new("dpkg")
            .arg("-s")
            .arg(package)
            .output()
            .map_err(|e| DaemonError::System(format!("Failed to check package status: {}", e)))?;

        Ok(output.status.success())
    }

    // Helper methods

    fn parse_package_info(&self, output: &str, package_name: &str) -> DaemonResult<PackageInfo> {
        let mut info = PackageInfo {
            name: package_name.to_string(),
            version: String::new(),
            description: String::new(),
            depends: Vec::new(),
            conflicts: Vec::new(),
            provides: Vec::new(),
            size: 0,
            priority: String::new(),
            section: String::new(),
        };

        for line in output.lines() {
            if let Some(version) = line.strip_prefix("Version: ") {
                info.version = version.trim().to_string();
            } else if let Some(description) = line.strip_prefix("Description: ") {
                info.description = description.trim().to_string();
            } else if let Some(depends) = line.strip_prefix("Depends: ") {
                info.depends = depends.split(", ").map(|s| s.trim().to_string()).collect();
            } else if let Some(conflicts) = line.strip_prefix("Conflicts: ") {
                info.conflicts = conflicts.split(", ").map(|s| s.trim().to_string()).collect();
            } else if let Some(provides) = line.strip_prefix("Provides: ") {
                info.provides = provides.split(", ").map(|s| s.trim().to_string()).collect();
            } else if let Some(priority) = line.strip_prefix("Priority: ") {
                info.priority = priority.trim().to_string();
            } else if let Some(section) = line.strip_prefix("Section: ") {
                info.section = section.trim().to_string();
            }
        }

        Ok(info)
    }

    fn parse_search_results(&self, output: &str) -> DaemonResult<Vec<PackageInfo>> {
        let mut packages = Vec::new();
        
        for line in output.lines() {
            if let Some((name, description)) = line.split_once(" - ") {
                let info = PackageInfo {
                    name: name.trim().to_string(),
                    version: String::new(), // Not available in search results
                    description: description.trim().to_string(),
                    depends: Vec::new(),
                    conflicts: Vec::new(),
                    provides: Vec::new(),
                    size: 0,
                    priority: String::new(),
                    section: String::new(),
                };
                packages.push(info);
            }
        }
        
        Ok(packages)
    }

    fn get_package_dependencies(&self, package: &str) -> DaemonResult<Vec<String>> {
        // Get package dependencies using apt-cache depends
        let output = Command::new("apt-cache")
            .arg("depends")
            .arg(package)
            .output()
            .map_err(|e| DaemonError::System(format!("Failed to get dependencies: {}", e)))?;

        if !output.status.success() {
            return Ok(Vec::new());
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        let mut deps = Vec::new();
        
        for line in output_str.lines() {
            if let Some(dep) = line.strip_prefix("  Depends: ") {
                let dep = dep.trim().to_string();
                if let Some(clean_dep) = dep.split_whitespace().next() {
                    deps.push(clean_dep.to_string());
                }
            }
        }
        
        Ok(deps)
    }

    /// Get cache status
    pub fn get_cache_status(&self) -> DaemonResult<String> {
        if self.cache_updated {
            Ok("up_to_date".to_string())
        } else {
            Ok("needs_update".to_string())
        }
    }

    /// Get package count
    pub fn get_package_count(&self) -> DaemonResult<u32> {
        // Run apt-cache stats to get package count
        let output = Command::new("apt-cache")
            .arg("stats")
            .output()
            .map_err(|e| DaemonError::System(format!("Failed to get package stats: {}", e)))?;

        if !output.status.success() {
            return Ok(0);
        }

        let output_str = String::from_utf8_lossy(&output.stdout);
        for line in output_str.lines() {
            if line.starts_with("Total package names:") {
                if let Some(count_str) = line.split(':').nth(1) {
                    if let Ok(count) = count_str.trim().parse::<u32>() {
                        return Ok(count);
                    }
                }
            }
        }
        
        Ok(0)
    }
}
