//! OSTree integration for apt-ostree compose
//! 
//! This module handles real OSTree operations including:
//! - Repository management
//! - Commit creation and management
//! - Tree operations
//! - Reference management
//! - Metadata handling

use std::path::PathBuf;
use std::process::Command;
use tokio::fs;
use apt_ostree::lib::error::{AptOstreeError, AptOstreeResult};
use crate::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 = if let Some(path) = repo_path {
            PathBuf::from(path)
        } else {
            workdir.join("ostree-repo")
        };
        
        Ok(Self {
            repo_path,
            workdir: workdir.clone(),
        })
    }
    
    /// Initialize OSTree repository
    pub async fn init_repository(&self) -> AptOstreeResult<()> {
        println!("Initializing OSTree repository at: {}", self.repo_path.display());
        
        // Create repository directory
        fs::create_dir_all(&self.repo_path).await
            .map_err(|e| AptOstreeError::System(format!("Failed to create repo dir: {}", e)))?;
        
        // Initialize OSTree repository
        let output = Command::new("ostree")
            .args([
                "init",
                "--repo",
                &self.repo_path.to_string_lossy(),
                "--mode=archive-z2"
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree init: {}", 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");
        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");
        
        // Create commit
        let mut args = vec![
            "commit",
            "--repo", &self.repo_path.to_string_lossy(),
            "--branch", &metadata.ref_name,
            "--tree", &format!("dir={}", build_root.display()),
        ];
        
        // Add parent if specified
        if let Some(parent_ref) = parent {
            args.extend_from_slice(&["--parent", parent_ref]);
        }
        
        // Add metadata
        if let Some(version) = &metadata.version {
            args.extend_from_slice(&["--add-metadata-string", &format!("version={}", version)]);
        }
        
        if let Some(description) = &metadata.description {
            args.extend_from_slice(&["--add-metadata-string", &format!("description={}", description)]);
        }
        
        if let Some(timestamp) = &metadata.timestamp {
            args.extend_from_slice(&["--add-metadata-string", &format!("timestamp={}", timestamp)]);
        }
        
        // Add commit message
        let commit_message = format!("apt-ostree compose: {}", metadata.ref_name);
        args.extend_from_slice(&["--subject", &commit_message]);
        
        let output = Command::new("ostree")
            .args(&args)
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run 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");
        
        println!("✅ OSTree commit created: {}", commit_hash);
        Ok(commit_hash.to_string())
    }
    
    /// 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 commit {}", ref_name, commit_hash);
        
        let output = Command::new("ostree")
            .args([
                "reset",
                "--repo", &self.repo_path.to_string_lossy(),
                ref_name,
                commit_hash
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree reset: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("ostree reset failed: {}", stderr)));
        }
        
        println!("✅ Reference {} updated to {}", ref_name, commit_hash);
        Ok(())
    }
    
    /// Create a summary file for the repository
    pub async fn create_summary(&self) -> AptOstreeResult<()> {
        println!("Creating OSTree repository summary...");
        
        let output = Command::new("ostree")
            .args([
                "summary",
                "--repo", &self.repo_path.to_string_lossy(),
                "--update"
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree summary: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("ostree summary failed: {}", stderr)));
        }
        
        println!("✅ Repository summary created");
        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 delta from {:?} to {}", from_ref, to_ref);
        
        let mut args = vec![
            "static-delta",
            "generate",
            "--repo", &self.repo_path.to_string_lossy(),
            "--to", to_ref,
        ];
        
        if let Some(from) = from_ref {
            args.extend_from_slice(&["--from", from]);
        }
        
        let output = Command::new("ostree")
            .args(&args)
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree static-delta: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            println!("Warning: static delta generation had issues: {}", stderr);
        } else {
            println!("✅ Static delta generated");
        }
        
        Ok(())
    }
    
    /// Export repository to a tar archive
    pub async fn export_archive(&self, output_path: &str, ref_name: &str) -> AptOstreeResult<()> {
        println!("Exporting OSTree repository to: {}", output_path);
        
        let output = Command::new("ostree")
            .args([
                "archive",
                "--repo", &self.repo_path.to_string_lossy(),
                "--ref", ref_name,
                output_path
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree archive: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("ostree archive failed: {}", stderr)));
        }
        
        println!("✅ Repository exported to {}", output_path);
        Ok(())
    }
    
    /// Get repository information
    pub async fn get_repo_info(&self) -> AptOstreeResult<String> {
        let output = Command::new("ostree")
            .args([
                "refs",
                "--repo", &self.repo_path.to_string_lossy()
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree refs: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("ostree refs failed: {}", stderr)));
        }
        
        let stdout = String::from_utf8_lossy(&output.stdout);
        Ok(stdout.to_string())
    }
    
    /// Check if a reference exists
    pub async fn reference_exists(&self, ref_name: &str) -> AptOstreeResult<bool> {
        let output = Command::new("ostree")
            .args([
                "rev-parse",
                "--repo", &self.repo_path.to_string_lossy(),
                ref_name
            ])
            .output();
        
        match output {
            Ok(output) => Ok(output.status.success()),
            Err(_) => Ok(false),
        }
    }
    
    /// 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")
            .args([
                "rev-parse",
                "--repo", &self.repo_path.to_string_lossy(),
                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")
            .args([
                "refs",
                "--repo", &self.repo_path.to_string_lossy()
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree refs: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("ostree refs failed: {}", 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 OSTree repository...");
        
        // Get all references
        let all_refs = self.list_references().await?;
        
        // Find references to remove (those not in keep_refs)
        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")
                .args([
                    "refs",
                    "--delete",
                    "--repo", &self.repo_path.to_string_lossy(),
                    &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")
            .args([
                "refs",
                "--repo", &self.repo_path.to_string_lossy(),
                "--gc"
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree gc: {}", 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(())
    }
}
