//! Container image generation for apt-ostree compose
//! 
//! This module handles OCI container image operations including:
//! - Container image creation from OSTree commits
//! - Layer management and optimization
//! - Image metadata and configuration
//! - Export to various container formats

use std::path::PathBuf;
use std::process::Command;
use tokio::fs;
use apt_ostree::lib::error::{AptOstreeError, AptOstreeResult};
use crate::treefile::OutputConfig;

/// Container image generator
pub struct ContainerGenerator {
    workdir: PathBuf,
    ostree_repo: PathBuf,
}

impl ContainerGenerator {
    /// Create a new container generator instance
    pub fn new(workdir: &PathBuf, ostree_repo: &PathBuf) -> Self {
        Self {
            workdir: workdir.clone(),
            ostree_repo: ostree_repo.clone(),
        }
    }
    
    /// Generate a container image from an OSTree commit
    pub async fn generate_image(&self, ref_name: &str, output_config: &OutputConfig) -> AptOstreeResult<()> {
        println!("Generating container image from OSTree reference: {}", ref_name);
        
        // Check if skopeo is available
        if !self.check_skopeo_available().await {
            return Err(AptOstreeError::System("skopeo is not available. Please install skopeo to generate container images.".to_string()));
        }
        
        // Create container working directory
        let container_dir = self.workdir.join("container");
        fs::create_dir_all(&container_dir).await
            .map_err(|e| AptOstreeError::System(format!("Failed to create container dir: {}", e)))?;
        
        // Extract OSTree tree to container directory
        self.extract_ostree_tree(ref_name, &container_dir).await?;
        
        // Generate container configuration
        self.generate_container_config(&container_dir, output_config).await?;
        
        // Create OCI image
        self.create_oci_image(&container_dir, ref_name, output_config).await?;
        
        println!("✅ Container image generated successfully");
        Ok(())
    }
    
    /// Check if skopeo is available
    async fn check_skopeo_available(&self) -> bool {
        Command::new("skopeo")
            .arg("--version")
            .output()
            .is_ok()
    }
    
    /// Extract OSTree tree to container directory
    async fn extract_ostree_tree(&self, ref_name: &str, container_dir: &PathBuf) -> AptOstreeResult<()> {
        println!("Extracting OSTree tree to container directory...");
        
        let output = Command::new("ostree")
            .args([
                "checkout",
                "--repo", &self.ostree_repo.to_string_lossy(),
                "--user-mode",
                ref_name,
                &container_dir.to_string_lossy()
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run ostree checkout: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("ostree checkout failed: {}", stderr)));
        }
        
        println!("✅ OSTree tree extracted");
        Ok(())
    }
    
    /// Generate container configuration files
    async fn generate_container_config(&self, container_dir: &PathBuf, output_config: &OutputConfig) -> AptOstreeResult<()> {
        println!("Generating container configuration...");
        
        // Create OCI layout
        let oci_dir = container_dir.join("oci");
        fs::create_dir_all(&oci_dir).await
            .map_err(|e| AptOstreeError::System(format!("Failed to create OCI dir: {}", e)))?;
        
        // Generate OCI layout
        self.generate_oci_layout(&oci_dir).await?;
        
        // Generate image configuration
        self.generate_image_config(&oci_dir, output_config).await?;
        
        // Generate manifest
        self.generate_manifest(&oci_dir).await?;
        
        println!("✅ Container configuration generated");
        Ok(())
    }
    
    /// Generate OCI layout structure
    async fn generate_oci_layout(&self, oci_dir: &PathBuf) -> AptOstreeResult<()> {
        let layout_content = r#"{
            "imageLayoutVersion": "1.0.0"
        }"#;
        
        fs::write(oci_dir.join("oci-layout"), layout_content).await
            .map_err(|e| AptOstreeError::System(format!("Failed to write oci-layout: {}", e)))?;
        
        // Create blobs directory
        fs::create_dir_all(oci_dir.join("blobs/sha256")).await
            .map_err(|e| AptOstreeError::System(format!("Failed to create blobs dir: {}", e)))?;
        
        Ok(())
    }
    
    /// Generate image configuration
    async fn generate_image_config(&self, oci_dir: &PathBuf, output_config: &OutputConfig) -> AptOstreeResult<()> {
        let config = serde_json::json!({
            "architecture": "amd64",
            "config": {
                "Cmd": ["/bin/bash"],
                "Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],
                "WorkingDir": "/",
                "Entrypoint": null
            },
            "created": chrono::Utc::now().to_rfc3339(),
            "history": [
                {
                    "created": chrono::Utc::now().to_rfc3339(),
                    "created_by": "apt-ostree compose",
                    "comment": "Generated by apt-ostree"
                }
            ],
            "os": "linux",
            "rootfs": {
                "type": "layers",
                "diff_ids": []
            }
        });
        
        let config_content = serde_json::to_string_pretty(&config)
            .map_err(|e| AptOstreeError::System(format!("Failed to serialize config: {}", e)))?;
        
        // Write config to blobs
        let config_hash = self.calculate_sha256(&config_content);
        let config_path = oci_dir.join(format!("blobs/sha256/{}", config_hash));
        fs::write(&config_path, config_content).await
            .map_err(|e| AptOstreeError::System(format!("Failed to write config blob: {}", e)))?;
        
        // Store config hash for manifest
        let config_hash_file = oci_dir.join("config_hash");
        fs::write(config_hash_file, &config_hash).await
            .map_err(|e| AptOstreeError::System(format!("Failed to write config hash: {}", e)))?;
        
        Ok(())
    }
    
    /// Generate OCI manifest
    async fn generate_manifest(&self, oci_dir: &PathBuf) -> AptOstreeResult<()> {
        // Read config hash
        let config_hash = fs::read_to_string(oci_dir.join("config_hash")).await
            .map_err(|e| AptOstreeError::System(format!("Failed to read config hash: {}", e)))?;
        
        let manifest = serde_json::json!({
            "schemaVersion": 2,
            "config": {
                "mediaType": "application/vnd.oci.image.config.v1+json",
                "digest": format!("sha256:{}", config_hash),
                "size": fs::metadata(oci_dir.join(format!("blobs/sha256/{}", config_hash))).await?.len()
            },
            "layers": []
        });
        
        let manifest_content = serde_json::to_string_pretty(&manifest)
            .map_err(|e| AptOstreeError::System(format!("Failed to serialize manifest: {}", e)))?;
        
        // Write manifest
        fs::write(oci_dir.join("manifest.json"), manifest_content).await
            .map_err(|e| AptOstreeError::System(format!("Failed to write manifest: {}", e)))?;
        
        Ok(())
    }
    
    /// Create OCI image using skopeo
    async fn create_oci_image(&self, container_dir: &PathBuf, ref_name: &str, output_config: &OutputConfig) -> AptOstreeResult<()> {
        println!("Creating OCI image...");
        
        let oci_dir = container_dir.join("oci");
        let output_path = if let Some(path) = &output_config.container_path {
            path.clone()
        } else {
            format!("{}.tar", ref_name.replace('/', "_"))
        };
        
        let output = Command::new("skopeo")
            .args([
                "copy",
                "--src", "dir",
                &oci_dir.to_string_lossy(),
                "docker-archive:".to_string() + &output_path
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run skopeo copy: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("skopeo copy failed: {}", stderr)));
        }
        
        println!("✅ OCI image created: {}", output_path);
        Ok(())
    }
    
    /// Calculate SHA256 hash of content
    fn calculate_sha256(&self, content: &str) -> String {
        use sha2::{Sha256, Digest};
        let mut hasher = Sha256::new();
        hasher.update(content.as_bytes());
        format!("{:x}", hasher.finalize())
    }
    
    /// Generate chunked container image
    pub async fn generate_chunked_image(&self, ref_name: &str, output_config: &OutputConfig) -> AptOstreeResult<()> {
        println!("Generating chunked container image...");
        
        // This would implement the chunked image generation logic
        // similar to rpm-ostree's build-chunked-oci command
        
        // For now, we'll use the standard image generation
        self.generate_image(ref_name, output_config).await
    }
    
    /// Export container image to different formats
    pub async fn export_image(&self, input_path: &str, output_format: &str, output_path: &str) -> AptOstreeResult<()> {
        println!("Exporting container image to {} format...", output_format);
        
        let output = Command::new("skopeo")
            .args([
                "copy",
                "--src", input_path,
                "--dest", &format!("{}:{}", output_format, output_path)
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run skopeo copy: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("skopeo export failed: {}", stderr)));
        }
        
        println!("✅ Image exported to {}: {}", output_format, output_path);
        Ok(())
    }
    
    /// Push container image to registry
    pub async fn push_image(&self, image_path: &str, registry_url: &str) -> AptOstreeResult<()> {
        println!("Pushing container image to registry: {}", registry_url);
        
        let output = Command::new("skopeo")
            .args([
                "copy",
                "--src", image_path,
                "--dest", registry_url
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run skopeo copy: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(AptOstreeError::System(format!("skopeo push failed: {}", stderr)));
        }
        
        println!("✅ Image pushed to registry: {}", registry_url);
        Ok(())
    }
    
    /// Validate container image
    pub async fn validate_image(&self, image_path: &str) -> AptOstreeResult<bool> {
        println!("Validating container image...");
        
        let output = Command::new("skopeo")
            .args([
                "inspect",
                image_path
            ])
            .output()
            .map_err(|e| AptOstreeError::System(format!("Failed to run skopeo inspect: {}", e)))?;
        
        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            println!("Image validation failed: {}", stderr);
            return Ok(false);
        }
        
        println!("✅ Image validation passed");
        Ok(true)
    }
}
