//! OSTree integration for apt-ostree compose

use std::path::PathBuf;
use std::process::Command;
use apt_ostree::lib::error::{AptOstreeError, AptOstreeResult};
use super::treefile::TreefileMetadata;

/// OSTree integration manager
pub struct OstreeIntegration {
    repo_path: PathBuf,
    workdir: PathBuf,
}

impl OstreeIntegration {
    /// Create a new OSTree integration instance
    pub fn new(repo_path: Option<&str>, workdir: &PathBuf) -> AptOstreeResult<Self> {
        let repo_path = repo_path.map(PathBuf::from).unwrap_or_else(|| {
            PathBuf::from("/var/lib/apt-ostree/repo")
        });
        
        Ok(Self {
            repo_path,
            workdir: workdir.clone(),
        })
    }
    
    /// Initialize OSTree repository
    pub async fn init_repository(&self) -> AptOstreeResult<()> {
        println!("Initializing OSTree repository...");
        
        // Create repository directory if it doesn't exist
        if !self.repo_path.exists() {
            std::fs::create_dir_all(&self.repo_path)
                .map_err(|e| AptOstreeError::System(format!("Failed to create repository directory: {}", e)))?;
        }
        
        // Initialize OSTree repository
        let output = Command::new("ostree")
            .arg("init")
            .arg("--repo")
            .arg(&self.repo_path)
            .arg("--mode")
            .arg("archive")
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to initialize OSTree repository: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("OSTree init failed: {}", stderr)));
        }
        
        println!("✅ OSTree repository initialized successfully");
        Ok(())
    }
    
    /// Create a new commit from the build directory
    pub async fn create_commit(&self, metadata: &TreefileMetadata, parent: Option<&str>) -> AptOstreeResult<String> {
        println!("Creating OSTree commit...");
        
        let build_root = self.workdir.join("build-root");
        if !build_root.exists() {
            return Err(AptOstreeError::System("Build root directory does not exist".to_string()));
        }
        
        // Prepare commit command
        let mut cmd = Command::new("ostree");
        cmd.arg("commit")
            .arg("--repo")
            .arg(&self.repo_path)
            .arg("--branch")
            .arg(&metadata.ref_name)
            .arg("--tree")
            .arg(&format!("dir={}", build_root.display()));
        
        // Add parent if specified
        if let Some(parent_ref) = parent {
            cmd.arg("--parent")
                .arg(parent_ref);
        }
        
        // Add metadata
        cmd.arg("--subject")
            .arg(&format!("apt-ostree compose: {}", metadata.ref_name))
            .arg("--body")
            .arg(&format!("Composed from treefile with ref: {}", metadata.ref_name));
        
        // Execute commit
        let output = cmd.output()
            .map_err(|e| AptOstreeError::System(format!("Failed to create OSTree commit: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("OSTree commit failed: {}", stderr)));
        }
        
        // Extract commit hash from output
        let stdout = String::from_utf8_lossy(&output.stdout);
        let commit_hash = stdout.lines()
            .find(|line| line.contains("commit"))
            .and_then(|line| line.split_whitespace().last())
            .unwrap_or("unknown")
            .to_string();
        
        println!("✅ OSTree commit created: {}", commit_hash);
        Ok(commit_hash)
    }
    
    /// Update a reference to point to a new commit
    pub async fn update_reference(&self, ref_name: &str, commit_hash: &str) -> AptOstreeResult<()> {
        println!("Updating reference {} to {}", ref_name, commit_hash);
        
        let output = Command::new("ostree")
            .arg("refs")
            .arg("--repo")
            .arg(&self.repo_path)
            .arg("--create")
            .arg(ref_name)
            .arg(commit_hash)
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to update reference: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("Failed to update reference: {}", stderr)));
        }
        
        println!("✅ Reference {} updated successfully", ref_name);
        Ok(())
    }
    
    /// Create a summary file for the repository
    pub async fn create_summary(&self) -> AptOstreeResult<()> {
        println!("Creating repository summary...");
        
        let output = Command::new("ostree")
            .arg("summary")
            .arg("--repo")
            .arg(&self.repo_path)
            .arg("--update")
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to create summary: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("Failed to create summary: {}", stderr)));
        }
        
        println!("✅ Repository summary created successfully");
        Ok(())
    }
    
    /// Generate static delta files for efficient updates
    pub async fn generate_static_deltas(&self, from_ref: Option<&str>, to_ref: &str) -> AptOstreeResult<()> {
        println!("Generating static deltas...");
        
        if let Some(from_ref) = from_ref {
            let output = Command::new("ostree")
                .arg("static-delta")
                .arg("generate")
                .arg("--repo")
                .arg(&self.repo_path)
                .arg("--from")
                .arg(from_ref)
                .arg("--to")
                .arg(to_ref)
                .output()
                .map_err(|e| AptOstreeError::System(format!("Failed to generate static delta: {}", e)))?;
            
            if !output.status.success() {
                let stderr = String::from_utf8_lossy(&output.stderr);
                return Err(AptOstreeError::System(format!("Failed to generate static delta: {}", stderr)));
            }
            
            println!("✅ Static delta generated successfully");
        } else {
            println!("No from reference specified, skipping static delta generation");
        }
        
        Ok(())
    }
    
    /// Export repository to a tar archive
    pub async fn export_archive(&self, output_path: &str, ref_name: &str) -> AptOstreeResult<()> {
        println!("Exporting archive...");
        
        let output = Command::new("ostree")
            .arg("export")
            .arg("--repo")
            .arg(&self.repo_path)
            .arg("--ref")
            .arg(ref_name)
            .arg("--subpath")
            .arg("/")
            .arg(output_path)
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to export archive: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("Failed to export archive: {}", stderr)));
        }
        
        println!("✅ Archive exported successfully to {}", output_path);
        Ok(())
    }
    
    /// Get repository information
    pub async fn get_repo_info(&self) -> AptOstreeResult<String> {
        println!("Getting repository info...");
        
        let output = Command::new("ostree")
            .arg("refs")
            .arg("--repo")
            .arg(&self.repo_path)
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to get repository info: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("Failed to get repository info: {}", stderr)));
        }
        
        let stdout = String::from_utf8_lossy(&output.stdout);
        let refs: Vec<String> = stdout.lines()
            .map(|line| line.trim().to_string())
            .filter(|line| !line.is_empty())
            .collect();
        
        let info = format!("Repository has {} references: {}", refs.len(), refs.join(", "));
        println!("✅ {}", info);
        Ok(info)
    }
    
    /// Check if a reference exists
    pub async fn reference_exists(&self, ref_name: &str) -> AptOstreeResult<bool> {
        let output = Command::new("ostree")
            .arg("refs")
            .arg("--repo")
            .arg(&self.repo_path)
            .arg("--list")
            .arg(ref_name)
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to check reference: {}", e)))?;
        
        Ok(output.status.success())
    }
    
    /// Get the commit hash for a reference
    pub async fn get_commit_hash(&self, ref_name: &str) -> AptOstreeResult<Option<String>> {
        let output = Command::new("ostree")
            .arg("rev-parse")
            .arg("--repo")
            .arg(&self.repo_path)
            .arg(ref_name)
            .output();
        
        match output {
            Ok(output) if output.status.success() => {
                let stdout = String::from_utf8_lossy(&output.stdout);
                Ok(Some(stdout.trim().to_string()))
            }
            _ => Ok(None)
        }
    }
    
    /// List all references in the repository
    pub async fn list_references(&self) -> AptOstreeResult<Vec<String>> {
        let output = Command::new("ostree")
            .arg("refs")
            .arg("--repo")
            .arg(&self.repo_path)
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to list references: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("Failed to list references: {}", stderr)));
        }
        
        let stdout = String::from_utf8_lossy(&output.stdout);
        let refs: Vec<String> = stdout.lines()
            .map(|line| line.trim().to_string())
            .filter(|line| !line.is_empty())
            .collect();
        
        Ok(refs)
    }
    
    /// Clean up old commits and objects
    pub async fn cleanup_repository(&self, keep_refs: &[String]) -> AptOstreeResult<()> {
        println!("Cleaning up repository...");
        
        // Get all references
        let all_refs = self.list_references().await?;
        
        // Find references to remove
        let refs_to_remove: Vec<String> = all_refs.into_iter()
            .filter(|ref_name| !keep_refs.contains(ref_name))
            .collect();
        
        for ref_name in refs_to_remove {
            println!("Removing reference: {}", ref_name);
            let output = Command::new("ostree")
                .arg("refs")
                .arg("--repo")
                .arg(&self.repo_path)
                .arg("--delete")
                .arg(&ref_name)
                .output();
            
            if let Ok(output) = output {
                if !output.status.success() {
                    let stderr = String::from_utf8_lossy(&output.stderr);
                    println!("Warning: Failed to remove reference {}: {}", ref_name, stderr);
                }
            }
        }
        
        // Run garbage collection
        let output = Command::new("ostree")
            .arg("refs")
            .arg("--repo")
            .arg(&self.repo_path)
            .arg("--gc")
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run garbage collection: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            println!("Warning: Garbage collection had issues: {}", stderr);
        }
        
        println!("✅ Repository cleanup completed");
        Ok(())
    }
}
