//! SBOM (Software Bill of Materials) generation and management handler

use anyhow::{anyhow, Context, Result};
use colored::Colorize;
use std::path::Path;
use syntaxis_core::{sbom::Sbom, ProjectConfig};
use tools_shared::find_config_path;

/// Handler for SBOM generation and management
#[derive(Debug)]
pub struct SbomHandler;

impl SbomHandler {
    /// Generate SBOM in configured formats
    pub fn generate_sbom() -> Result<()> {
        println!("📦 Generating Software Bill of Materials (SBOM)...\n");

        // Load configuration
        let config_path = Self::find_config()
            .context("Configuration not found. Run 'init' and 'create' first")?;
        let config = ProjectConfig::load_from_file(&config_path)
            .context("Failed to load project configuration")?;

        // Check if SBOM is enabled
        if !config.sbom.enabled {
            println!(
                "  {} SBOM generation is disabled in configuration",
                "⚠".yellow()
            );
            println!("  Set [sbom] enabled = true in lifecycle.toml to enable");
            return Ok(());
        }

        // Config directory is the parent of the config file
        let config_dir = config_path
            .parent()
            .unwrap_or_else(|| Path::new(".project"));

        println!("  {} Project: {}", "•".cyan(), config.project.name.bold());
        println!(
            "  {} Format: {}",
            "•".cyan(),
            format!("{}", config.sbom.format).green()
        );
        println!(
            "  {} Include transitive: {}",
            "•".cyan(),
            config.sbom.include_transitive.to_string().green()
        );

        // Generate SBOM from project root (parent of config dir)
        let project_root = config_dir.parent().unwrap_or_else(|| Path::new("."));

        // Generate SBOM
        println!("\n  {} Generating SBOM...", "•".cyan());
        let sbom = Sbom::generate(project_root, &config.project.name, config.sbom.format)
            .context("Failed to generate SBOM")?;

        println!("    ✓ SBOM generated successfully");
        println!(
            "    {} Direct dependencies: {}",
            "•".cyan(),
            sbom.direct_dependencies.to_string().green()
        );
        println!(
            "    {} Transitive dependencies: {}",
            "•".cyan(),
            sbom.transitive_dependencies.to_string().green()
        );
        println!(
            "    {} Total components: {}",
            "•".cyan(),
            sbom.components.len().to_string().green()
        );

        // Display generation info
        if let Some(gen_info) = &sbom.generation_info {
            println!("\n  {} Generation Info:", "•".cyan());
            println!("    Method: {}", gen_info.generation_method.green());
            println!(
                "    Formats: {}",
                gen_info.formats_generated.join(", ").green()
            );

            if !gen_info.alternative_methods_tried.is_empty() {
                println!(
                    "    Alternative methods tried: {}",
                    gen_info.alternative_methods_tried.join(", ").yellow()
                );
            }

            if !gen_info.warnings.is_empty() {
                println!("\n  {} Warnings:", "⚠".yellow());
                for warning in &gen_info.warnings {
                    println!("    • {}", warning);
                }
            }
        }

        // Save SBOM to file in config directory
        println!("\n  {} Saving SBOM to file...", "•".cyan());
        let sbom_dir = config_dir.join("sbom");
        std::fs::create_dir_all(&sbom_dir).context("Failed to create sbom directory")?;

        let sbom_file = sbom_dir.join(format!(
            "sbom-{}.json",
            chrono::Local::now().format("%Y%m%d_%H%M%S")
        ));
        sbom.save_to_file(&sbom_file)
            .context("Failed to save SBOM file")?;

        println!(
            "    ✓ Saved to: {}",
            sbom_file.display().to_string().green()
        );

        println!("\n✅ SBOM generation completed!");

        Ok(())
    }

    /// Generate cargo tree visualization
    pub fn generate_tree() -> Result<()> {
        println!("🌳 Generating dependency tree...\n");

        // Load configuration
        let config_path = Self::find_config()
            .context("Configuration not found. Run 'init' and 'create' first")?;
        let config = ProjectConfig::load_from_file(&config_path)
            .context("Failed to load project configuration")?;

        // Config directory is the parent of the config file
        let config_dir = config_path
            .parent()
            .unwrap_or_else(|| Path::new(".project"));

        // Project root is parent of config dir
        let project_root = config_dir.parent().unwrap_or_else(|| Path::new("."));

        println!("  {} Project: {}", "•".cyan(), config.project.name.bold());

        // Run cargo tree
        println!("\n  {} Running cargo tree...", "•".cyan());
        let output = std::process::Command::new("cargo")
            .args(["tree"])
            .current_dir(project_root)
            .output()
            .context("Failed to run cargo tree")?;

        if !output.status.success() {
            return Err(anyhow!(
                "cargo tree failed - ensure you're in a Rust project directory"
            ));
        }

        let tree_output = String::from_utf8_lossy(&output.stdout);

        // Display tree
        println!("\n{}", "═══════════════════════════════════════".cyan());
        println!("{}", "DEPENDENCY TREE".bold().cyan());
        println!("{}", "═══════════════════════════════════════".cyan());
        println!("{}", tree_output);

        // Save tree to file in config directory
        println!("  {} Saving tree to file...", "•".cyan());
        let tree_dir = config_dir.join("sbom");
        std::fs::create_dir_all(&tree_dir).context("Failed to create sbom directory")?;

        let tree_file = tree_dir.join(format!(
            "tree-{}.txt",
            chrono::Local::now().format("%Y%m%d_%H%M%S")
        ));
        std::fs::write(&tree_file, &*tree_output).context("Failed to save tree file")?;

        println!(
            "    ✓ Saved to: {}",
            tree_file.display().to_string().green()
        );

        println!("\n✅ Dependency tree generation completed!");

        Ok(())
    }

    /// Find configuration file
    fn find_config() -> Result<std::path::PathBuf> {
        find_config_path("config.toml", None).ok_or_else(|| anyhow!("Configuration file not found"))
    }
}
