//! Shared library backend for IPC operations and package management

use crate::commands::Command;
use apt_ostree::lib::error::{AptOstreeError, AptOstreeResult};

use std::process::Command as ProcessCommand;

/// Operating system information structure
#[derive(Debug, Clone)]
struct OsInfo {
    distribution: String,
    version: String,
    codename: String,
}

/// Kernel information structure
#[derive(Debug, Clone)]
struct KernelInfo {
    version: String,
    release: String,
}

/// ShlibBackend command - Shared library backend for IPC operations and package management
pub struct ShlibBackendCommand;

impl ShlibBackendCommand {
    pub fn new() -> Self {
        ShlibBackendCommand
    }
}

#[cfg(feature = "development")]
impl Command for ShlibBackendCommand {
    fn execute(&self, args: &[String]) -> AptOstreeResult<()> {
        if args.is_empty() || args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) {
            self.show_help();
            return Ok(());
        }

        let subcommand = &args[0];
        let sub_args = &args[1..];

        match subcommand.as_str() {
            "get-basearch" => self.handle_get_basearch(sub_args),
            "varsubst-basearch" => self.handle_varsubst_basearch(sub_args),
            "packagelist-from-commit" => self.handle_packagelist_from_commit(sub_args),
            _ => {
                println!("Unknown shlib-backend subcommand: {}", subcommand);
                println!("Use 'apt-ostree shlib-backend --help' for available subcommands");
                Ok(())
            }
        }
    }

    fn name(&self) -> &'static str {
        "shlib-backend"
    }

    fn description(&self) -> &'static str {
        "Shared library backend for IPC operations and package management"
    }

    fn show_help(&self) {
        println!("Usage: apt-ostree shlib-backend <SUBCOMMAND> [OPTIONS]");
        println!();
        println!("Shared library backend for apt-ostree.");
        println!("These commands handle IPC operations and package management.");
        println!();
        println!("Subcommands:");
        println!("  get-basearch              Get base architecture using APT");
        println!("  varsubst-basearch        Variable substitution for architecture");
        println!("  packagelist-from-commit  Extract package list from OSTree commit");
        println!();
        println!("Options:");
        println!("  --help, -h               Show this help message");
        println!();
        println!("Examples:");
        println!("  apt-ostree shlib-backend get-basearch");
        println!("  apt-ostree shlib-backend varsubst-basearch 'arch={{arch}}'");
        println!("  apt-ostree shlib-backend packagelist-from-commit <commit-hash>");
    }
}

impl ShlibBackendCommand {
    fn handle_get_basearch(&self, _args: &[String]) -> AptOstreeResult<()> {
        // Print the Debian base architecture to stdout
        let arch = self.get_system_architecture()?;
        println!("{}", arch);
        Ok(())
    }

    fn handle_varsubst_basearch(&self, args: &[String]) -> AptOstreeResult<()> {
        if args.is_empty() {
            return Err(AptOstreeError::InvalidArgument("varsubst-basearch requires source string argument".to_string()));
        }

        let source = &args[0];
        let result = self.substitute_variables(source)?;
        println!("{}", result);
        Ok(())
    }

    fn handle_packagelist_from_commit(&self, args: &[String]) -> AptOstreeResult<()> {
        if args.is_empty() {
            return Err(AptOstreeError::InvalidArgument("packagelist-from-commit requires commit argument".to_string()));
        }

        let commit = &args[0];
        // Use ostree CLI as fallback since we're blocked on Rust bindings
        let packages = self.get_packages_from_commit_via_cli(commit)?;
        
        // Output in a format compatible with expected IPC response
        for package in packages {
            println!("{}", package);
        }
        
        Ok(())
    }

    fn get_system_architecture(&self) -> AptOstreeResult<String> {
        // Enhanced architecture detection with multiple fallbacks
        let mut arch = None;
        
        // Try dpkg first (most reliable on Debian systems)
        if let Ok(output) = ProcessCommand::new("dpkg")
            .arg("--print-architecture")
            .output() {
            if output.status.success() {
                let dpkg_arch = String::from_utf8_lossy(&output.stdout).trim().to_string();
                if !dpkg_arch.is_empty() {
                    arch = Some(dpkg_arch);
                }
            }
        }
        
        // Fallback to uname if dpkg fails
        if arch.is_none() {
            if let Ok(output) = ProcessCommand::new("uname")
                .arg("-m")
                .output() {
                if output.status.success() {
                    let uname_arch = String::from_utf8_lossy(&output.stdout).trim().to_string();
                    if !uname_arch.is_empty() {
                        arch = Some(uname_arch);
                    }
                }
            }
        }
        
        // Fallback to environment variable
        if arch.is_none() {
            if let Ok(env_arch) = std::env::var("DEB_HOST_ARCH") {
                if !env_arch.is_empty() {
                    arch = Some(env_arch);
                }
            }
        }
        
        // Final fallback to hardcoded common architectures
        if arch.is_none() {
            if cfg!(target_arch = "x86_64") {
                arch = Some("amd64".to_string());
            } else if cfg!(target_arch = "aarch64") {
                arch = Some("arm64".to_string());
            } else if cfg!(target_arch = "arm") {
                arch = Some("armhf".to_string());
            } else if cfg!(target_arch = "riscv64") {
                arch = Some("riscv64".to_string());
            }
        }
        
        arch.ok_or_else(|| AptOstreeError::System("Could not determine system architecture".to_string()))
    }

    fn substitute_variables(&self, source: &str) -> AptOstreeResult<String> {
        // Enhanced variable substitution with comprehensive system information
        let mut result = source.to_string();

        let arch = self.get_system_architecture()?;
        let os_info = self.get_os_info()?;
        let kernel_info = self.get_kernel_info()?;

        // Support multiple token styles and comprehensive variables
        let replacements: [(&str, String); 15] = [
            // Architecture variables
            ("{arch}", arch.clone()),
            ("{{arch}}", arch.clone()),
            ("{ARCH}", arch.to_uppercase()),
            ("{{ARCH}}", arch.to_uppercase()),
            ("{basearch}", arch.clone()),
            ("{BASEARCH}", arch.to_uppercase()),
            
            // Operating system variables
            ("{os}", os_info.distribution.clone()),
            ("{OS}", os_info.distribution.to_uppercase()),
            ("{version}", os_info.version.clone()),
            ("{VERSION}", os_info.version.to_uppercase()),
            ("{codename}", os_info.codename.clone()),
            ("{CODENAME}", os_info.codename.to_uppercase()),
            
            // Kernel variables
            ("{kernel}", kernel_info.version.clone()),
            ("{KERNEL}", kernel_info.version.to_uppercase()),
            ("{release}", kernel_info.release.clone()),
        ];

        for (pat, val) in replacements {
            result = result.replace(pat, &val);
        }

        Ok(result)
    }

    /// Get operating system information
    fn get_os_info(&self) -> AptOstreeResult<OsInfo> {
        // Try to read from /etc/os-release first
        if let Ok(content) = std::fs::read_to_string("/etc/os-release") {
            let mut distribution = "debian".to_string();
            let mut version = "unknown".to_string();
            let mut codename = "unknown".to_string();
            
            for line in content.lines() {
                if line.starts_with("ID=") {
                    distribution = line[3..].trim_matches('"').to_string();
                } else if line.starts_with("VERSION_ID=") {
                    version = line[12..].trim_matches('"').to_string();
                } else if line.starts_with("VERSION_CODENAME=") {
                    codename = line[17..].trim_matches('"').to_string();
                }
            }
            
            return Ok(OsInfo { distribution, version, codename });
        }
        
        // Fallback to lsb_release if available
        if let Ok(output) = ProcessCommand::new("lsb_release")
            .args(["-a"])
            .output() {
            if output.status.success() {
                let output_str = String::from_utf8_lossy(&output.stdout);
                let mut distribution = "debian".to_string();
                let mut version = "unknown".to_string();
                let mut codename = "unknown".to_string();
                
                for line in output_str.lines() {
                    if line.starts_with("Distributor ID:") {
                        distribution = line[16..].trim().to_lowercase();
                    } else if line.starts_with("Release:") {
                        version = line[9..].trim().to_string();
                    } else if line.starts_with("Codename:") {
                        codename = line[10..].trim().to_string();
                    }
                }
                
                return Ok(OsInfo { distribution, version, codename });
            }
        }
        
        // Final fallback
        Ok(OsInfo {
            distribution: "debian".to_string(),
            version: "unknown".to_string(),
            codename: "unknown".to_string(),
        })
    }

    /// Get kernel information
    fn get_kernel_info(&self) -> AptOstreeResult<KernelInfo> {
        // Get kernel version from uname
        let version = if let Ok(output) = ProcessCommand::new("uname")
            .arg("-r")
            .output() {
            if output.status.success() {
                String::from_utf8_lossy(&output.stdout).trim().to_string()
            } else {
                "unknown".to_string()
            }
        } else {
            "unknown".to_string()
        };
        
        // Get kernel release from uname -v
        let release = if let Ok(output) = ProcessCommand::new("uname")
            .arg("-v")
            .output() {
            if output.status.success() {
                String::from_utf8_lossy(&output.stdout).trim().to_string()
            } else {
                "unknown".to_string()
            }
        } else {
            "unknown".to_string()
        };
        
        Ok(KernelInfo { version, release })
    }

    // TODO: Re-enable when implementing real package extraction
    // fn get_packages_from_commit(&self, _commit: &str) -> AptOstreeResult<Vec<String>> {
    //     // Simulate package list for stub
    //     Ok(vec![
    //         "apt".to_string(),
    //         "ostree".to_string(),
    //         "systemd".to_string(),
    //         "bash".to_string(),
    //         "coreutils".to_string(),
    //     ])
    // }

    fn get_packages_from_commit_via_cli(&self, commit: &str) -> AptOstreeResult<Vec<String>> {
        // Try to get package list from OSTree commit using CLI commands
        // This is a fallback approach while we resolve Rust binding issues
        
        // First, try to get commit metadata
        let output = ProcessCommand::new("ostree")
            .args(["show", "--print-metadata-key", "apt.packages", commit])
            .output();
            
        match output {
            Ok(output) if output.status.success() => {
                let packages_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
                if !packages_str.is_empty() {
                    // Parse comma-separated package list
                    let packages: Vec<String> = packages_str
                        .split(',')
                        .map(|s| s.trim().to_string())
                        .filter(|s| !s.is_empty())
                        .collect();
                    return Ok(packages);
                }
            }
            _ => {}
        }
        
        // Fallback: try to get commit info and extract package names from commit message
        let output = ProcessCommand::new("ostree")
            .args(["log", commit])
            .output();
            
        match output {
            Ok(output) if output.status.success() => {
                let log_output = String::from_utf8_lossy(&output.stdout);
                // Look for package-related patterns in commit messages
                let packages = self.extract_packages_from_log(&log_output);
                if !packages.is_empty() {
                    return Ok(packages);
                }
            }
            _ => {}
        }
        
        // Final fallback: return empty list if we can't extract packages
        Ok(Vec::new())
    }

    fn extract_packages_from_log(&self, log_output: &str) -> Vec<String> {
        // Extract package names from OSTree commit log
        // Look for common patterns like "package: name" or "installed: name"
        let mut packages = Vec::new();
        
        for line in log_output.lines() {
            let line = line.trim();
            
            // Look for package installation patterns
            if line.contains("package:") || line.contains("installed:") || line.contains("apt:") {
                if let Some(pkg_name) = self.extract_package_name(line) {
                    packages.push(pkg_name);
                }
            }
        }
        
        packages
    }

    fn extract_package_name(&self, line: &str) -> Option<String> {
        // Extract package name from various formats
        let patterns = [
            "package:",
            "installed:",
            "apt:",
            "dpkg:",
        ];
        
        for pattern in &patterns {
            if let Some(pos) = line.find(pattern) {
                let after_pattern = &line[pos + pattern.len()..];
                let package_name = after_pattern.split_whitespace().next()?;
                if !package_name.is_empty() {
                    return Some(package_name.to_string());
                }
            }
        }
        
        None
    }

    // TODO: Re-enable when implementing real IPC operations
    // fn create_ipc_socket(&self) -> AptOstreeResult<i32> {
    //     // Get IPC file descriptor from environment
    //     let fd_str = env::var("APT_OSTREE_SHLIB_IPC_FD")
    //     .map_err(|_| AptOstreeError::System("APT_OSTREE_SHLIB_IPC_FD environment variable not set".to_string()))?;
    //     
    //     let fd = fd_str.parse::<i32>()
    //     .map_err(|_| AptOstreeError::System("Invalid IPC file descriptor".to_string()))?;
    // 
    //     Ok(fd)
    // }
    // 
    // fn send_memfd_result(&self, _data: &[u8]) -> AptOstreeResult<()> {
    //     // TODO: Implement real memfd result sending
    //     // This is a stub implementation - add to todo for later implementation
    //     println!("Status: Stub implementation");
    //     println!("Next: Implement real memfd result sending");
    //     println!("  - Create sealed memfd for data transfer");
    //     println!("  - Send via Unix domain socket");
    //     println!("  - Handle secure descriptor passing");
    // 
    //     Ok(())
    // }
}
