//! Package management utilities for different operating systems.
//!
//! This module provides a unified interface for package management across different platforms
//! (Ubuntu/Debian with APT, macOS with Homebrew). It includes platform detection, package
//! installation/removal, updates, and package search functionality.
//!
//! # Example
//!
//! ```no_run
//! use herolib_os::os::{PackHero, Platform};
//!
//! let mut pkg = PackHero::new();
//! if pkg.platform() == Platform::MacOS {
//!     pkg.install("curl")?;
//! }
//! # Ok::<(), herolib_os::os::PackageError>(())
//! ```

use std::process::Command;

/// A structure to hold command execution results.
///
/// Contains the captured stdout, stderr, exit code, and success status of a command.
#[derive(Debug, Clone)]
pub struct CommandResult {
    /// Standard output from the command
    pub stdout: String,
    /// Standard error output from the command
    pub stderr: String,
    /// Whether the command completed successfully (exit code 0)
    pub success: bool,
    /// Exit code returned by the command
    pub code: i32,
}

/// Error type for package management operations
#[derive(Debug)]
pub enum PackageError {
    /// Command failed with error message
    CommandFailed(String),
    /// Command execution failed with IO error
    CommandExecutionFailed(std::io::Error),
    /// Unsupported platform
    UnsupportedPlatform(String),
    /// Other error
    Other(String),
}

impl std::fmt::Display for PackageError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            PackageError::CommandFailed(msg) => write!(f, "Command failed: {}", msg),
            PackageError::CommandExecutionFailed(e) => write!(f, "Command execution failed: {}", e),
            PackageError::UnsupportedPlatform(msg) => write!(f, "Unsupported platform: {}", msg),
            PackageError::Other(msg) => write!(f, "Error: {}", msg),
        }
    }
}

impl std::error::Error for PackageError {}

/// Platform enum for detecting the current operating system
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Platform {
    /// Ubuntu Linux
    Ubuntu,
    /// macOS
    MacOS,
    /// Unknown platform
    Unknown,
}

impl Platform {
    /// Detect the current platform
    pub fn detect() -> Self {
        // Check for macOS
        if std::path::Path::new("/usr/bin/sw_vers").exists() {
            return Platform::MacOS;
        }

        // Check for Ubuntu
        if std::path::Path::new("/etc/lsb-release").exists() {
            // Read the file to confirm it's Ubuntu
            if let Ok(content) = std::fs::read_to_string("/etc/lsb-release") {
                if content.contains("Ubuntu") {
                    return Platform::Ubuntu;
                }
            }
        }

        Platform::Unknown
    }
}

// Thread-local storage for debug flag
thread_local! {
    static DEBUG: std::cell::RefCell<bool> = std::cell::RefCell::new(false);
}

/// Sets the debug flag for the current thread.
///
/// This is used internally to control debug output for package operations.
pub fn set_thread_local_debug(debug: bool) {
    DEBUG.with(|cell| {
        *cell.borrow_mut() = debug;
    });
}

/// Gets the debug flag for the current thread.
pub fn thread_local_debug() -> bool {
    DEBUG.with(|cell| *cell.borrow())
}

/// Executes a package management command and returns the result.
///
/// This is a low-level function that executes arbitrary package management commands.
/// It captures both stdout and stderr and returns them along with the exit code.
///
/// # Arguments
///
/// * `args` - Command line arguments (first element is the executable)
/// * `debug` - If `true`, prints the command being executed
pub fn execute_package_command(args: &[&str], debug: bool) -> Result<CommandResult, PackageError> {
    // Save the current debug flag
    let previous_debug = thread_local_debug();

    // Set the thread-local debug flag
    set_thread_local_debug(debug);

    if debug {
        println!("Executing command: {}", args.join(" "));
    }

    let output = Command::new(args[0]).args(&args[1..]).output();

    // Restore the previous debug flag
    set_thread_local_debug(previous_debug);

    match output {
        Ok(output) => {
            let stdout = String::from_utf8_lossy(&output.stdout).to_string();
            let stderr = String::from_utf8_lossy(&output.stderr).to_string();

            let result = CommandResult {
                stdout,
                stderr,
                success: output.status.success(),
                code: output.status.code().unwrap_or(-1),
            };

            // Always output stdout/stderr when debug is true
            if debug {
                if !result.stdout.is_empty() {
                    println!("Command stdout: {}", result.stdout);
                }

                if !result.stderr.is_empty() {
                    println!("Command stderr: {}", result.stderr);
                }

                if result.success {
                    println!("Command succeeded with code {}", result.code);
                } else {
                    println!("Command failed with code {}", result.code);
                }
            }

            if result.success {
                Ok(result)
            } else {
                // If command failed and debug is false, output stderr
                if !debug {
                    println!(
                        "Command failed with code {}: {}",
                        result.code,
                        result.stderr.trim()
                    );
                }
                Err(PackageError::CommandFailed(format!(
                    "Command failed with code {}: {}",
                    result.code,
                    result.stderr.trim()
                )))
            }
        }
        Err(e) => {
            // Always output error information
            println!("Command execution failed: {}", e);
            Err(PackageError::CommandExecutionFailed(e))
        }
    }
}

/// Trait for package managers
/// Trait for platform-specific package management operations.
///
/// This trait defines a common interface for package managers across different platforms.
/// Each platform (Ubuntu, macOS) implements this trait with their specific package manager.
pub trait PackageManager {
    /// Installs a package on the system.
    ///
    /// # Arguments
    ///
    /// * `package` - The name of the package to install
    fn install(&self, package: &str) -> Result<CommandResult, PackageError>;

    /// Removes an installed package from the system.
    ///
    /// # Arguments
    ///
    /// * `package` - The name of the package to remove
    fn remove(&self, package: &str) -> Result<CommandResult, PackageError>;

    /// Updates the package manager's package lists from repositories.
    fn update(&self) -> Result<CommandResult, PackageError>;

    /// Upgrades all installed packages to their latest versions.
    fn upgrade(&self) -> Result<CommandResult, PackageError>;

    /// Lists all installed packages on the system.
    ///
    /// # Returns
    ///
    /// A vector of package names that are currently installed.
    fn list_installed(&self) -> Result<Vec<String>, PackageError>;

    /// Searches for packages matching the given query.
    ///
    /// # Arguments
    ///
    /// * `query` - A search term or pattern to find packages
    fn search(&self, query: &str) -> Result<Vec<String>, PackageError>;

    /// Checks if a specific package is installed on the system.
    ///
    /// # Arguments
    ///
    /// * `package` - The name of the package to check
    fn is_installed(&self, package: &str) -> Result<bool, PackageError>;
}

/// APT package manager implementation for Ubuntu/Debian systems.
///
/// This struct implements the `PackageManager` trait using the APT package manager,
/// which is the standard package management system for Ubuntu and Debian-based distributions.
pub struct AptPackageManager {
    /// Whether to print debug output for package operations
    debug: bool,
}

impl AptPackageManager {
    /// Creates a new APT package manager instance.
    ///
    /// # Arguments
    ///
    /// * `debug` - If `true`, prints debug information for package operations
    pub fn new(debug: bool) -> Self {
        Self { debug }
    }
}

impl PackageManager for AptPackageManager {
    fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
        // Use -y to make it non-interactive and --quiet to reduce output
        execute_package_command(
            &["apt-get", "install", "-y", "--quiet", package],
            self.debug,
        )
    }

    fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
        // Use -y to make it non-interactive and --quiet to reduce output
        execute_package_command(&["apt-get", "remove", "-y", "--quiet", package], self.debug)
    }

    fn update(&self) -> Result<CommandResult, PackageError> {
        // Use -y to make it non-interactive and --quiet to reduce output
        execute_package_command(&["apt-get", "update", "-y", "--quiet"], self.debug)
    }

    fn upgrade(&self) -> Result<CommandResult, PackageError> {
        // Use -y to make it non-interactive and --quiet to reduce output
        execute_package_command(&["apt-get", "upgrade", "-y", "--quiet"], self.debug)
    }

    fn list_installed(&self) -> Result<Vec<String>, PackageError> {
        let result = execute_package_command(&["dpkg", "--get-selections"], self.debug)?;
        let packages = result
            .stdout
            .lines()
            .filter_map(|line| {
                let parts: Vec<&str> = line.split_whitespace().collect();
                if parts.len() >= 2 && parts[1] == "install" {
                    Some(parts[0].to_string())
                } else {
                    None
                }
            })
            .collect();
        Ok(packages)
    }

    fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
        let result = execute_package_command(&["apt-cache", "search", query], self.debug)?;
        let packages = result
            .stdout
            .lines()
            .map(|line| {
                let parts: Vec<&str> = line.split_whitespace().collect();
                if !parts.is_empty() {
                    parts[0].to_string()
                } else {
                    String::new()
                }
            })
            .filter(|s| !s.is_empty())
            .collect();
        Ok(packages)
    }

    fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
        let result = execute_package_command(&["dpkg", "-s", package], self.debug);
        match result {
            Ok(cmd_result) => Ok(cmd_result.success),
            Err(_) => Ok(false),
        }
    }
}

/// Homebrew package manager implementation for macOS systems.
///
/// This struct implements the `PackageManager` trait using Homebrew,
/// which is the standard package manager for macOS.
pub struct BrewPackageManager {
    /// Whether to print debug output for package operations
    debug: bool,
}

impl BrewPackageManager {
    /// Creates a new Homebrew package manager instance.
    ///
    /// # Arguments
    ///
    /// * `debug` - If `true`, prints debug information for package operations
    pub fn new(debug: bool) -> Self {
        Self { debug }
    }
}

impl PackageManager for BrewPackageManager {
    fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
        // Use --quiet to reduce output
        execute_package_command(&["brew", "install", "--quiet", package], self.debug)
    }

    fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
        // Use --quiet to reduce output
        execute_package_command(&["brew", "uninstall", "--quiet", package], self.debug)
    }

    fn update(&self) -> Result<CommandResult, PackageError> {
        // Use --quiet to reduce output
        execute_package_command(&["brew", "update", "--quiet"], self.debug)
    }

    fn upgrade(&self) -> Result<CommandResult, PackageError> {
        // Use --quiet to reduce output
        execute_package_command(&["brew", "upgrade", "--quiet"], self.debug)
    }

    fn list_installed(&self) -> Result<Vec<String>, PackageError> {
        let result = execute_package_command(&["brew", "list", "--formula"], self.debug)?;
        let packages = result
            .stdout
            .lines()
            .map(|line| line.trim().to_string())
            .filter(|s| !s.is_empty())
            .collect();
        Ok(packages)
    }

    fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
        let result = execute_package_command(&["brew", "search", query], self.debug)?;
        let packages = result
            .stdout
            .lines()
            .map(|line| line.trim().to_string())
            .filter(|s| !s.is_empty())
            .collect();
        Ok(packages)
    }

    fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
        let result = execute_package_command(&["brew", "list", package], self.debug);
        match result {
            Ok(cmd_result) => Ok(cmd_result.success),
            Err(_) => Ok(false),
        }
    }
}

/// Cross-platform package manager factory.
///
/// PackHero automatically detects the current platform and provides a unified interface
/// for package management operations. It handles platform-specific details transparently,
/// allowing the same code to work on both Ubuntu (APT) and macOS (Homebrew).
///
/// # Example
///
/// ```no_run
/// use herolib_os::os::PackHero;
///
/// let mut pkg = PackHero::new();
/// pkg.set_debug(true);
/// pkg.install("curl")?;
/// pkg.update()?;
/// let installed = pkg.list_installed()?;
/// # Ok::<(), herolib_os::os::PackageError>(())
/// ```
pub struct PackHero {
    /// The detected platform
    platform: Platform,
    /// Whether to print debug output
    debug: bool,
}

impl PackHero {
    /// Creates a new PackHero instance with automatic platform detection.
    ///
    /// Platform detection is done immediately, and the detected platform is cached
    /// for the lifetime of this instance.
    pub fn new() -> Self {
        let platform = Platform::detect();
        Self {
            platform,
            debug: false,
        }
    }

    /// Sets the debug mode and returns a mutable reference for method chaining.
    ///
    /// When debug mode is enabled, package operations will print diagnostic information.
    pub fn set_debug(&mut self, debug: bool) -> &mut Self {
        self.debug = debug;
        self
    }

    /// Returns whether debug mode is currently enabled.
    pub fn debug(&self) -> bool {
        self.debug
    }

    /// Returns the detected platform for this instance.
    pub fn platform(&self) -> Platform {
        self.platform
    }

    /// Gets the appropriate package manager for the detected platform.
    ///
    /// # Returns
    ///
    /// `Ok(Box<dyn PackageManager>)` with the platform-specific implementation,
    /// or `Err(PackageError::UnsupportedPlatform)` if the platform is unknown.
    fn get_package_manager(&self) -> Result<Box<dyn PackageManager>, PackageError> {
        match self.platform {
            Platform::Ubuntu => Ok(Box::new(AptPackageManager::new(self.debug))),
            Platform::MacOS => Ok(Box::new(BrewPackageManager::new(self.debug))),
            Platform::Unknown => Err(PackageError::UnsupportedPlatform(
                "Unsupported platform".to_string(),
            )),
        }
    }

    /// Installs a package using the platform's package manager.
    ///
    /// # Arguments
    ///
    /// * `package` - The name of the package to install
    pub fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
        let pm = self.get_package_manager()?;
        pm.install(package)
    }

    /// Removes an installed package using the platform's package manager.
    ///
    /// # Arguments
    ///
    /// * `package` - The name of the package to remove
    pub fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
        let pm = self.get_package_manager()?;
        pm.remove(package)
    }

    /// Updates the package manager's repository lists.
    ///
    /// On Ubuntu: Updates APT package lists.
    /// On macOS: Updates Homebrew package lists.
    pub fn update(&self) -> Result<CommandResult, PackageError> {
        let pm = self.get_package_manager()?;
        pm.update()
    }

    /// Upgrades all installed packages to their latest versions.
    ///
    /// On Ubuntu: Runs `apt-get upgrade`.
    /// On macOS: Runs `brew upgrade`.
    pub fn upgrade(&self) -> Result<CommandResult, PackageError> {
        let pm = self.get_package_manager()?;
        pm.upgrade()
    }

    /// Lists all installed packages on the system.
    ///
    /// # Returns
    ///
    /// A vector of package names currently installed.
    pub fn list_installed(&self) -> Result<Vec<String>, PackageError> {
        let pm = self.get_package_manager()?;
        pm.list_installed()
    }

    /// Searches for packages matching the given query.
    ///
    /// # Arguments
    ///
    /// * `query` - A search term or pattern to find packages
    pub fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
        let pm = self.get_package_manager()?;
        pm.search(query)
    }

    /// Checks if a specific package is currently installed.
    ///
    /// # Arguments
    ///
    /// * `package` - The name of the package to check
    pub fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
        let pm = self.get_package_manager()?;
        pm.is_installed(package)
    }
}

#[cfg(test)]
mod tests {
    // Import the std::process::Command directly for some test-specific commands
    use super::*;
    use std::process::Command as StdCommand;
    use std::sync::{Arc, Mutex};

    #[test]
    fn test_platform_detection() {
        // Test that platform detection returns a valid platform
        let platform = Platform::detect();
        println!("Detected platform: {:?}", platform);

        // Verify that we get one of the expected platform values
        match platform {
            Platform::Ubuntu | Platform::MacOS | Platform::Unknown => {
                // All valid platforms
            }
        }

        // Test that detection is consistent (calling it twice should return the same result)
        let platform2 = Platform::detect();
        assert_eq!(platform, platform2);

        // Test that the platform detection logic makes sense for the current environment
        match platform {
            Platform::MacOS => {
                // If detected as macOS, sw_vers should exist
                assert!(std::path::Path::new("/usr/bin/sw_vers").exists());
            }
            Platform::Ubuntu => {
                // If detected as Ubuntu, lsb-release should exist and contain "Ubuntu"
                assert!(std::path::Path::new("/etc/lsb-release").exists());
                if let Ok(content) = std::fs::read_to_string("/etc/lsb-release") {
                    assert!(content.contains("Ubuntu"));
                }
            }
            Platform::Unknown => {
                // If unknown, neither macOS nor Ubuntu indicators should be present
                // (or Ubuntu file exists but doesn't contain "Ubuntu")
                if std::path::Path::new("/usr/bin/sw_vers").exists() {
                    // This shouldn't happen - if sw_vers exists, it should be detected as macOS
                    panic!("sw_vers exists but platform detected as Unknown");
                }
            }
        }
    }

    #[test]
    fn test_debug_flag() {
        // Test setting and getting the debug flag
        set_thread_local_debug(true);
        assert_eq!(thread_local_debug(), true);

        set_thread_local_debug(false);
        assert_eq!(thread_local_debug(), false);
    }

    #[test]
    fn test_package_error_display() {
        // Test the Display implementation for PackageError
        let err1 = PackageError::CommandFailed("command failed".to_string());
        assert_eq!(err1.to_string(), "Command failed: command failed");

        let err2 = PackageError::UnsupportedPlatform("test platform".to_string());
        assert_eq!(err2.to_string(), "Unsupported platform: test platform");

        let err3 = PackageError::Other("other error".to_string());
        assert_eq!(err3.to_string(), "Error: other error");

        // We can't easily test CommandExecutionFailed because std::io::Error doesn't implement PartialEq
    }

    // Mock package manager for testing
    struct MockPackageManager {
        // debug field is kept for consistency with real package managers
        #[allow(dead_code)]
        debug: bool,
        install_called: Arc<Mutex<bool>>,
        remove_called: Arc<Mutex<bool>>,
        update_called: Arc<Mutex<bool>>,
        upgrade_called: Arc<Mutex<bool>>,
        list_installed_called: Arc<Mutex<bool>>,
        search_called: Arc<Mutex<bool>>,
        is_installed_called: Arc<Mutex<bool>>,
        // Control what the mock returns
        should_succeed: bool,
    }

    impl MockPackageManager {
        fn new(debug: bool, should_succeed: bool) -> Self {
            Self {
                debug,
                install_called: Arc::new(Mutex::new(false)),
                remove_called: Arc::new(Mutex::new(false)),
                update_called: Arc::new(Mutex::new(false)),
                upgrade_called: Arc::new(Mutex::new(false)),
                list_installed_called: Arc::new(Mutex::new(false)),
                search_called: Arc::new(Mutex::new(false)),
                is_installed_called: Arc::new(Mutex::new(false)),
                should_succeed,
            }
        }
    }

    impl PackageManager for MockPackageManager {
        fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
            *self.install_called.lock().unwrap() = true;
            if self.should_succeed {
                Ok(CommandResult {
                    stdout: format!("Installed package {}", package),
                    stderr: String::new(),
                    success: true,
                    code: 0,
                })
            } else {
                Err(PackageError::CommandFailed(
                    "Mock install failed".to_string(),
                ))
            }
        }

        fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
            *self.remove_called.lock().unwrap() = true;
            if self.should_succeed {
                Ok(CommandResult {
                    stdout: format!("Removed package {}", package),
                    stderr: String::new(),
                    success: true,
                    code: 0,
                })
            } else {
                Err(PackageError::CommandFailed(
                    "Mock remove failed".to_string(),
                ))
            }
        }

        fn update(&self) -> Result<CommandResult, PackageError> {
            *self.update_called.lock().unwrap() = true;
            if self.should_succeed {
                Ok(CommandResult {
                    stdout: "Updated package lists".to_string(),
                    stderr: String::new(),
                    success: true,
                    code: 0,
                })
            } else {
                Err(PackageError::CommandFailed(
                    "Mock update failed".to_string(),
                ))
            }
        }

        fn upgrade(&self) -> Result<CommandResult, PackageError> {
            *self.upgrade_called.lock().unwrap() = true;
            if self.should_succeed {
                Ok(CommandResult {
                    stdout: "Upgraded packages".to_string(),
                    stderr: String::new(),
                    success: true,
                    code: 0,
                })
            } else {
                Err(PackageError::CommandFailed(
                    "Mock upgrade failed".to_string(),
                ))
            }
        }

        fn list_installed(&self) -> Result<Vec<String>, PackageError> {
            *self.list_installed_called.lock().unwrap() = true;
            if self.should_succeed {
                Ok(vec!["package1".to_string(), "package2".to_string()])
            } else {
                Err(PackageError::CommandFailed(
                    "Mock list_installed failed".to_string(),
                ))
            }
        }

        fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
            *self.search_called.lock().unwrap() = true;
            if self.should_succeed {
                Ok(vec![
                    format!("result1-{}", query),
                    format!("result2-{}", query),
                ])
            } else {
                Err(PackageError::CommandFailed(
                    "Mock search failed".to_string(),
                ))
            }
        }

        fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
            *self.is_installed_called.lock().unwrap() = true;
            if self.should_succeed {
                Ok(package == "installed-package")
            } else {
                Err(PackageError::CommandFailed(
                    "Mock is_installed failed".to_string(),
                ))
            }
        }
    }

    // Custom PackHero for testing with a mock package manager
    struct TestPackHero {
        platform: Platform,
        #[allow(dead_code)]
        debug: bool,
        mock_manager: MockPackageManager,
    }

    impl TestPackHero {
        fn new(platform: Platform, debug: bool, should_succeed: bool) -> Self {
            Self {
                platform,
                debug,
                mock_manager: MockPackageManager::new(debug, should_succeed),
            }
        }

        fn get_package_manager(&self) -> Result<&dyn PackageManager, PackageError> {
            match self.platform {
                Platform::Ubuntu | Platform::MacOS => Ok(&self.mock_manager),
                Platform::Unknown => Err(PackageError::UnsupportedPlatform(
                    "Unsupported platform".to_string(),
                )),
            }
        }

        fn install(&self, package: &str) -> Result<CommandResult, PackageError> {
            let pm = self.get_package_manager()?;
            pm.install(package)
        }

        fn remove(&self, package: &str) -> Result<CommandResult, PackageError> {
            let pm = self.get_package_manager()?;
            pm.remove(package)
        }

        fn update(&self) -> Result<CommandResult, PackageError> {
            let pm = self.get_package_manager()?;
            pm.update()
        }

        fn upgrade(&self) -> Result<CommandResult, PackageError> {
            let pm = self.get_package_manager()?;
            pm.upgrade()
        }

        fn list_installed(&self) -> Result<Vec<String>, PackageError> {
            let pm = self.get_package_manager()?;
            pm.list_installed()
        }

        fn search(&self, query: &str) -> Result<Vec<String>, PackageError> {
            let pm = self.get_package_manager()?;
            pm.search(query)
        }

        fn is_installed(&self, package: &str) -> Result<bool, PackageError> {
            let pm = self.get_package_manager()?;
            pm.is_installed(package)
        }
    }

    #[test]
    fn test_packhero_with_mock_success() {
        // Test PackHero with a mock package manager that succeeds
        let hero = TestPackHero::new(Platform::Ubuntu, false, true);

        // Test install
        let result = hero.install("test-package");
        assert!(result.is_ok());
        assert!(*hero.mock_manager.install_called.lock().unwrap());

        // Test remove
        let result = hero.remove("test-package");
        assert!(result.is_ok());
        assert!(*hero.mock_manager.remove_called.lock().unwrap());

        // Test update
        let result = hero.update();
        assert!(result.is_ok());
        assert!(*hero.mock_manager.update_called.lock().unwrap());

        // Test upgrade
        let result = hero.upgrade();
        assert!(result.is_ok());
        assert!(*hero.mock_manager.upgrade_called.lock().unwrap());

        // Test list_installed
        let result = hero.list_installed();
        assert!(result.is_ok());
        assert_eq!(
            result.unwrap(),
            vec!["package1".to_string(), "package2".to_string()]
        );
        assert!(*hero.mock_manager.list_installed_called.lock().unwrap());

        // Test search
        let result = hero.search("query");
        assert!(result.is_ok());
        assert_eq!(
            result.unwrap(),
            vec!["result1-query".to_string(), "result2-query".to_string()]
        );
        assert!(*hero.mock_manager.search_called.lock().unwrap());

        // Test is_installed
        let result = hero.is_installed("installed-package");
        assert!(result.is_ok());
        assert!(result.unwrap());
        assert!(*hero.mock_manager.is_installed_called.lock().unwrap());

        let result = hero.is_installed("not-installed-package");
        assert!(result.is_ok());
        assert!(!result.unwrap());
    }

    #[test]
    fn test_packhero_with_mock_failure() {
        // Test PackHero with a mock package manager that fails
        let hero = TestPackHero::new(Platform::Ubuntu, false, false);

        // Test install
        let result = hero.install("test-package");
        assert!(result.is_err());
        assert!(*hero.mock_manager.install_called.lock().unwrap());

        // Test remove
        let result = hero.remove("test-package");
        assert!(result.is_err());
        assert!(*hero.mock_manager.remove_called.lock().unwrap());

        // Test update
        let result = hero.update();
        assert!(result.is_err());
        assert!(*hero.mock_manager.update_called.lock().unwrap());

        // Test upgrade
        let result = hero.upgrade();
        assert!(result.is_err());
        assert!(*hero.mock_manager.upgrade_called.lock().unwrap());

        // Test list_installed
        let result = hero.list_installed();
        assert!(result.is_err());
        assert!(*hero.mock_manager.list_installed_called.lock().unwrap());

        // Test search
        let result = hero.search("query");
        assert!(result.is_err());
        assert!(*hero.mock_manager.search_called.lock().unwrap());

        // Test is_installed
        let result = hero.is_installed("installed-package");
        assert!(result.is_err());
        assert!(*hero.mock_manager.is_installed_called.lock().unwrap());
    }

    #[test]
    fn test_packhero_unsupported_platform() {
        // Test PackHero with an unsupported platform
        let hero = TestPackHero::new(Platform::Unknown, false, true);

        // All operations should fail with UnsupportedPlatform error
        let result = hero.install("test-package");
        assert!(result.is_err());
        match result {
            Err(PackageError::UnsupportedPlatform(_)) => (),
            _ => panic!("Expected UnsupportedPlatform error"),
        }

        let result = hero.remove("test-package");
        assert!(result.is_err());
        match result {
            Err(PackageError::UnsupportedPlatform(_)) => (),
            _ => panic!("Expected UnsupportedPlatform error"),
        }

        let result = hero.update();
        assert!(result.is_err());
        match result {
            Err(PackageError::UnsupportedPlatform(_)) => (),
            _ => panic!("Expected UnsupportedPlatform error"),
        }
    }

    // Real-world tests that actually install and remove packages on Ubuntu
    // These tests will only run on Ubuntu and will be skipped on other platforms
    #[test]
    fn test_real_package_operations_on_ubuntu() {
        // Check if we're on Ubuntu
        let platform = Platform::detect();
        if platform != Platform::Ubuntu {
            println!(
                "Skipping real package operations test on non-Ubuntu platform: {:?}",
                platform
            );
            return;
        }

        println!("Running real package operations test on Ubuntu");

        // Create a PackHero instance with debug enabled
        let mut hero = PackHero::new();
        hero.set_debug(true);

        // Test package to install/remove
        let test_package = "wget";

        // First, check if the package is already installed
        let is_installed_before = match hero.is_installed(test_package) {
            Ok(result) => result,
            Err(e) => {
                println!("Error checking if package is installed: {}", e);
                return;
            }
        };

        println!(
            "Package {} is installed before test: {}",
            test_package, is_installed_before
        );

        // If the package is already installed, we'll remove it first
        if is_installed_before {
            println!("Removing existing package {} before test", test_package);
            match hero.remove(test_package) {
                Ok(_) => println!("Successfully removed package {}", test_package),
                Err(e) => {
                    println!("Error removing package {}: {}", test_package, e);
                    return;
                }
            }

            // Verify it was removed
            match hero.is_installed(test_package) {
                Ok(is_installed) => {
                    if is_installed {
                        println!("Failed to remove package {}", test_package);
                        return;
                    } else {
                        println!("Verified package {} was removed", test_package);
                    }
                }
                Err(e) => {
                    println!(
                        "Error checking if package is installed after removal: {}",
                        e
                    );
                    return;
                }
            }
        }

        // Now install the package
        println!("Installing package {}", test_package);
        match hero.install(test_package) {
            Ok(_) => println!("Successfully installed package {}", test_package),
            Err(e) => {
                println!("Error installing package {}: {}", test_package, e);
                return;
            }
        }

        // Verify it was installed
        match hero.is_installed(test_package) {
            Ok(is_installed) => {
                if !is_installed {
                    println!("Failed to install package {}", test_package);
                    return;
                } else {
                    println!("Verified package {} was installed", test_package);
                }
            }
            Err(e) => {
                println!(
                    "Error checking if package is installed after installation: {}",
                    e
                );
                return;
            }
        }

        // Test the search functionality
        println!("Searching for packages with 'wget'");
        match hero.search("wget") {
            Ok(results) => {
                println!("Search results: {:?}", results);
                assert!(
                    results.iter().any(|r| r.contains("wget")),
                    "Search results should contain wget"
                );
            }
            Err(e) => {
                println!("Error searching for packages: {}", e);
                return;
            }
        }

        // Test listing installed packages
        println!("Listing installed packages");
        match hero.list_installed() {
            Ok(packages) => {
                println!("Found {} installed packages", packages.len());
                // Check if our test package is in the list
                assert!(
                    packages.iter().any(|p| p == test_package),
                    "Installed packages list should contain {}",
                    test_package
                );
            }
            Err(e) => {
                println!("Error listing installed packages: {}", e);
                return;
            }
        }

        // Now remove the package if it wasn't installed before
        if !is_installed_before {
            println!("Removing package {} after test", test_package);
            match hero.remove(test_package) {
                Ok(_) => println!("Successfully removed package {}", test_package),
                Err(e) => {
                    println!("Error removing package {}: {}", test_package, e);
                    return;
                }
            }

            // Verify it was removed
            match hero.is_installed(test_package) {
                Ok(is_installed) => {
                    if is_installed {
                        println!("Failed to remove package {}", test_package);
                        return;
                    } else {
                        println!("Verified package {} was removed", test_package);
                    }
                }
                Err(e) => {
                    println!(
                        "Error checking if package is installed after removal: {}",
                        e
                    );
                    return;
                }
            }
        }

        // Test update functionality
        println!("Testing package list update");
        match hero.update() {
            Ok(_) => println!("Successfully updated package lists"),
            Err(e) => {
                println!("Error updating package lists: {}", e);
                return;
            }
        }

        println!("All real package operations tests passed on Ubuntu");
    }

    // Test to check if apt-get is available on the system
    #[test]
    fn test_apt_get_availability() {
        // This test checks if apt-get is available on the system
        let output = StdCommand::new("which")
            .arg("apt-get")
            .output()
            .expect("Failed to execute which apt-get");

        let success = output.status.success();
        let stdout = String::from_utf8_lossy(&output.stdout).to_string();

        println!("apt-get available: {}", success);
        if success {
            println!("apt-get path: {}", stdout.trim());
        }

        // On Ubuntu, this should pass
        if Platform::detect() == Platform::Ubuntu {
            assert!(success, "apt-get should be available on Ubuntu");
        }
    }
}
