use crate::lib::error::{AptOstreeError, AptOstreeResult};
use std::process::Command;
use std::fs;
use std::path::Path;


/// Manager for OSTree operations
#[allow(dead_code)]
pub struct OstreeManager {
    sysroot_path: String,
}

impl OstreeManager {
    /// Create a new OSTree manager
    pub fn new() -> Self {
        Self {
            sysroot_path: "/".to_string(),
        }
    }
}

impl Default for OstreeManager {
    fn default() -> Self {
        Self::new()
    }
}

impl OstreeManager {
    /// Check if OSTree is available on the system
    pub fn is_available(&self) -> bool {
        // Check if ostree binary exists and can be executed
        Command::new("ostree")
            .arg("--version")
            .output()
            .is_ok()
    }
    
    /// Get system information
    pub fn get_system_info(&self) -> SystemInfo {
        SystemInfo {
            os: self.get_os_info(),
            kernel: self.get_kernel_version(),
            architecture: self.get_architecture(),
            kernel_cmdline: self.get_kernel_cmdline(),
        }
    }
    
    /// List deployments with real OSTree data
    pub fn list_deployments(&self) -> AptOstreeResult<Vec<DeploymentInfo>> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        // Check if this is an OSTree-booted system
        if !self.is_ostree_booted() {
            // Return a default deployment for non-OSTree systems
            return Ok(vec![
                DeploymentInfo {
                    id: "system".to_string(),
                    commit: "not-ostree".to_string(),
                    version: "traditional".to_string(),
                    is_current: true,
                    timestamp: None,
                    checksum: None,
                    origin: None,
                    booted: true,
                    staged: false,
                    pending: false,
                    rollback: false,
                }
            ]);
        }
        
        // Use ostree admin status to get deployment information
        let output = Command::new("ostree")
            .arg("admin")
            .arg("status")
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to get OSTree status: {}", e)))?;
        
        if !output.status.success() {
            // If ostree admin status fails, try to provide basic info
            let stderr = String::from_utf8_lossy(&output.stderr);
            if stderr.contains("No such file or directory") {
                return Ok(vec![
                    DeploymentInfo {
                        id: "system".to_string(),
                        commit: "not-ostree".to_string(),
                        version: "traditional".to_string(),
                        is_current: true,
                        timestamp: None,
                        checksum: None,
                        origin: None,
                        booted: true,
                        staged: false,
                        pending: false,
                        rollback: false,
                    }
                ]);
            }
            return Err(AptOstreeError::System("Failed to get OSTree status".to_string()));
        }
        
        let status_output = String::from_utf8_lossy(&output.stdout);
        self.parse_ostree_status(&status_output)
    }
    
    /// Get the current deployment
    pub fn get_current_deployment(&self) -> AptOstreeResult<Option<DeploymentInfo>> {
        let deployments = self.list_deployments()?;
        Ok(deployments.into_iter().find(|d| d.booted))
    }
    
    /// Create a new deployment
    pub fn create_deployment(&self, ref_name: &str, options: &DeploymentOptions) -> AptOstreeResult<String> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        // Build ostree admin deploy command
        let mut cmd = Command::new("ostree");
        cmd.arg("admin")
            .arg("deploy");
        
        if options.allow_downgrade {
            cmd.arg("--allow-downgrade");
        }
        
        if options.require_signatures {
            cmd.arg("--require-signatures");
        }
        
        if let Some(origin) = &options.origin {
            cmd.arg("--origin").arg(origin);
        }
        
        cmd.arg(ref_name);
        
        let output = cmd.output()
            .map_err(|e| AptOstreeError::System(format!("Failed to create deployment: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("Deployment failed: {}", stderr)));
        }
        
        // Extract commit hash from output
        let stdout = String::from_utf8_lossy(&output.stdout);
        if let Some(commit) = stdout.lines()
            .find(|line| line.contains("Commit:"))
            .and_then(|line| line.split_whitespace().nth(1)) {
            Ok(commit.to_string())
        } else {
            Ok("unknown".to_string())
        }
    }
    
    /// Switch to a different deployment
    pub fn switch_deployment(&self, deployment_id: &str) -> AptOstreeResult<()> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        let output = Command::new("ostree")
            .arg("admin")
            .arg("deploy")
            .arg(deployment_id)
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to switch deployment: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("Failed to switch deployment: {}", stderr)));
        }
        
        Ok(())
    }
    
    /// Rollback to previous deployment
    pub fn rollback_deployment(&self) -> AptOstreeResult<String> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        let output = Command::new("ostree")
            .arg("admin")
            .arg("rollback")
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to rollback deployment: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("Rollback failed: {}", stderr)));
        }
        
        // Extract rollback deployment info
        let stdout = String::from_utf8_lossy(&output.stdout);
        if let Some(deployment) = stdout.lines()
            .find(|line| line.contains("Rolled back to:"))
            .and_then(|line| line.split_whitespace().nth(3)) {
            Ok(deployment.to_string())
        } else {
            Ok("unknown".to_string())
        }
    }
    
    /// Get OSTree repository information
    pub fn get_repo_info(&self) -> AptOstreeResult<RepoInfo> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        let output = Command::new("ostree")
            .arg("refs")
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to get OSTree refs: {}", e)))?;
        
        if !output.status.success() {
            return Err(AptOstreeError::System("Failed to get OSTree refs".to_string()));
        }
        
        let refs_output = String::from_utf8_lossy(&output.stdout);
        let refs: Vec<String> = refs_output
            .lines()
            .map(|line| line.trim().to_string())
            .filter(|line| !line.is_empty())
            .collect();
        
        Ok(RepoInfo {
            refs,
            path: "/ostree/repo".to_string(),
        })
    }
    
    /// Get system status for daemon
    pub fn get_system_status(&self) -> AptOstreeResult<String> {
        if !self.is_available() {
            return Ok("not-available".to_string());
        }
        
        if !self.is_ostree_booted() {
            return Ok("not-booted".to_string());
        }
        
        let deployments = self.list_deployments()?;
        let current = deployments.iter().find(|d| d.booted);
        
        if let Some(deployment) = current {
            Ok(format!("booted:{}", deployment.id))
        } else {
            Ok("unknown".to_string())
        }
    }
    
    /// Get kernel arguments for a deployment
    pub fn get_kernel_args(&self, deployment_index: Option<usize>) -> AptOstreeResult<Vec<String>> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        if !self.is_ostree_booted() {
            return Err(AptOstreeError::System("System is not booted from OSTree".to_string()));
        }
        
        // Get deployments to determine target
        let deployments = self.list_deployments()?;
        let _target_deployment = if let Some(index) = deployment_index {
            if index >= deployments.len() {
                return Err(AptOstreeError::System(
                    format!("Invalid deployment index: {}. Available deployments: 0-{}", 
                        index, deployments.len() - 1)
                ));
            }
            &deployments[index]
        } else {
            // Use current deployment
            deployments.iter()
                .find(|d| d.booted)
                .ok_or_else(|| AptOstreeError::System("No booted deployment found".to_string()))?
        };
        
        // Read kernel arguments from /proc/cmdline for current boot
        if deployment_index.is_none() {
            // For current deployment, read from /proc/cmdline
            let cmdline = std::fs::read_to_string("/proc/cmdline")
                .map_err(|e| AptOstreeError::System(format!("Failed to read /proc/cmdline: {}", e)))?;
            
            let args: Vec<String> = cmdline
                .split_whitespace()
                .map(|s| s.to_string())
                .collect();
            
            return Ok(args);
        }
        
        // For other deployments, we would need to read from OSTree metadata
        // For now, return a placeholder
        Ok(vec!["console=tty0".to_string(), "quiet".to_string()])
    }
    
    /// Set kernel arguments for a deployment
    pub fn set_kernel_args(&self, deployment_index: Option<usize>, args: &[String]) -> AptOstreeResult<()> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        if !self.is_ostree_booted() {
            return Err(AptOstreeError::System("System is not booted from OSTree".to_string()));
        }
        
        // Get deployments to determine target
        let deployments = self.list_deployments()?;
        let target_deployment = if let Some(index) = deployment_index {
            if index >= deployments.len() {
                return Err(AptOstreeError::System(
                    format!("Invalid deployment index: {}. Available deployments: 0-{}", 
                        index, deployments.len() - 1)
                ));
            }
            &deployments[index]
        } else {
            // Use current deployment
            deployments.iter()
                .find(|d| d.booted)
                .ok_or_else(|| AptOstreeError::System("No booted deployment found".to_string()))?
        };
        
        // For now, simulate setting kernel arguments
        // In a real implementation, this would use OSTree's deployment_set_kargs
        println!("Setting kernel arguments for deployment {}: {}", 
            target_deployment.id, args.join(" "));
        
        // TODO: Implement real OSTree kernel argument setting
        // This would involve:
        // 1. Creating a staged deployment
        // 2. Setting the kernel arguments
        // 3. Finalizing the deployment
        
        Ok(())
    }
    
    /// Append kernel arguments to a deployment
    pub fn append_kernel_args(&self, deployment_index: Option<usize>, args: &[String]) -> AptOstreeResult<()> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        if !self.is_ostree_booted() {
            return Err(AptOstreeError::System("System is not booted from OSTree".to_string()));
        }
        
        // Get current kernel arguments
        let mut current_args = self.get_kernel_args(deployment_index)?;
        
        // Append new arguments
        for arg in args {
            if !current_args.contains(arg) {
                current_args.push(arg.clone());
            }
        }
        
        // Set the updated arguments
        self.set_kernel_args(deployment_index, &current_args)
    }
    
    /// Delete kernel arguments from a deployment
    pub fn delete_kernel_args(&self, deployment_index: Option<usize>, args: &[String]) -> AptOstreeResult<()> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        if !self.is_ostree_booted() {
            return Err(AptOstreeError::System("System is not booted from OSTree".to_string()));
        }
        
        // Get current kernel arguments
        let mut current_args = self.get_kernel_args(deployment_index)?;
        
        // Remove specified arguments
        for arg in args {
            current_args.retain(|existing| existing != arg);
        }
        
        // Set the updated arguments
        self.set_kernel_args(deployment_index, &current_args)
    }
    
    /// Replace kernel arguments in a deployment
    pub fn replace_kernel_args(&self, deployment_index: Option<usize>, old_arg: &str, new_arg: &str) -> AptOstreeResult<()> {
        if !self.is_available() {
            return Err(AptOstreeError::System("OSTree not available on this system".to_string()));
        }
        
        if !self.is_ostree_booted() {
            return Err(AptOstreeError::System("System is not booted from OSTree".to_string()));
        }
        
        // Get current kernel arguments
        let mut current_args = self.get_kernel_args(deployment_index)?;
        
        // Replace the old argument with the new one
        for arg in &mut current_args {
            if arg == old_arg {
                *arg = new_arg.to_string();
            }
        }
        
        // Set the updated arguments
        self.set_kernel_args(deployment_index, &current_args)
    }
    
    /// Get OS information from /etc/os-release
    fn get_os_info(&self) -> String {
        let os_release = fs::read_to_string("/etc/os-release")
            .unwrap_or_else(|_| "Unknown".to_string());
        
        for line in os_release.lines() {
            if line.starts_with("PRETTY_NAME=") {
                let value = line.split_once('=').map(|x| x.1).unwrap_or("Unknown");
                return value.trim_matches('"').to_string();
            }
        }
        "Unknown".to_string()
    }
    
    /// Get kernel version from /proc/version
    fn get_kernel_version(&self) -> String {
        fs::read_to_string("/proc/version")
            .unwrap_or_else(|_| "Unknown".to_string())
            .split_whitespace()
            .nth(2)
            .unwrap_or("Unknown")
            .to_string()
    }
    
    /// Get system architecture
    fn get_architecture(&self) -> String {
        Command::new("uname")
            .arg("-m")
            .output()
            .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_string())
            .unwrap_or_else(|_| "Unknown".to_string())
    }
    
    /// Get kernel command line from /proc/cmdline
    fn get_kernel_cmdline(&self) -> String {
        fs::read_to_string("/proc/cmdline")
            .unwrap_or_else(|_| "Unknown".to_string())
            .trim()
            .to_string()
    }
    
    /// Parse OSTree status output to extract deployment information
    fn parse_ostree_status(&self, status_output: &str) -> AptOstreeResult<Vec<DeploymentInfo>> {
        let mut deployments = Vec::new();
        
        for line in status_output.lines() {
            if line.contains("*") || line.trim().starts_with("ostree=") {
                // This is a deployment line
                if let Some(deployment) = self.parse_deployment_line(line) {
                    deployments.push(deployment);
                }
            }
        }
        
        // If no deployments found, create a default one
        if deployments.is_empty() {
            deployments.push(DeploymentInfo {
                id: "default".to_string(),
                commit: "unknown".to_string(),
                version: "unknown".to_string(),
                is_current: false,
                timestamp: None,
                checksum: None,
                origin: None,
                booted: true,
                staged: false,
                pending: false,
                rollback: false,
            });
        }
        
        Ok(deployments)
    }
    
    /// Parse a single deployment line from OSTree status output
    fn parse_deployment_line(&self, line: &str) -> Option<DeploymentInfo> {
        // Example line: "* ostree=abc123:debian/stable/x86_64/standard"
        let parts: Vec<&str> = line.split_whitespace().collect();
        if parts.len() < 2 {
            return None;
        }
        
        let ostree_part = parts[1];
        if !ostree_part.starts_with("ostree=") {
            return None;
        }
        
        let commit_part = ostree_part.strip_prefix("ostree=")?;
        let commit_parts: Vec<&str> = commit_part.split(':').collect();
        
        if commit_parts.len() < 2 {
            return None;
        }
        
        let commit = commit_parts[0];
        let ref_path = commit_parts[1];
        let ref_parts: Vec<&str> = ref_path.split('/').collect();
        
        let version = if ref_parts.len() >= 2 {
            format!("{}/{}", ref_parts[0], ref_parts[1])
        } else {
            "unknown".to_string()
        };
        
        let is_current = line.contains("*");
        
        Some(DeploymentInfo {
            id: ref_path.to_string(),
            commit: commit.to_string(),
            version,
            is_current,
            timestamp: None, // TODO: Extract from commit metadata
            checksum: Some(commit.to_string()),
            origin: Some(ref_path.to_string()),
            booted: is_current,
            staged: false, // TODO: Detect staged deployments
            pending: false, // TODO: Detect pending deployments
            rollback: false, // TODO: Detect rollback deployments
        })
    }
    
    /// Check if the system is booted from OSTree
    pub fn is_ostree_booted(&self) -> bool {
        Path::new("/run/ostree-booted").exists() || Path::new("/ostree").exists()
    }
}

/// System information
#[derive(Debug, Clone)]
pub struct SystemInfo {
    pub os: String,
    pub kernel: String,
    pub architecture: String,
    pub kernel_cmdline: String,
}

/// Deployment information with enhanced metadata
#[derive(Debug, Clone)]
pub struct DeploymentInfo {
    pub id: String,
    pub commit: String,
    pub version: String,
    pub is_current: bool,
    pub timestamp: Option<String>,
    pub checksum: Option<String>,
    pub origin: Option<String>,
    pub booted: bool,
    pub staged: bool,
    pub pending: bool,
    pub rollback: bool,
}

/// Repository information
#[derive(Debug)]
pub struct RepoInfo {
    pub refs: Vec<String>,
    pub path: String,
}

/// Deployment options for creating new deployments
#[derive(Debug, Clone)]
pub struct DeploymentOptions {
    pub allow_downgrade: bool,
    pub require_signatures: bool,
    pub origin: Option<String>,
}

impl Default for DeploymentOptions {
    fn default() -> Self {
        Self {
            allow_downgrade: false,
            require_signatures: true,
            origin: None,
        }
    }
}
