//! Command handlers for Aphoria CLI

use std::process::ExitCode;

use aphoria::AphoriaConfig;

use crate::cli::Commands;

mod claims;
mod corpus;
mod eval;
mod extractors;
mod governance;
mod lifecycle;
mod patterns;
mod policy;
mod policy_ops;
mod research;
mod scan;
mod scope;
mod shadow;
mod utils;
mod verify;

// Re-export for public API compatibility.
// These are used by the CLI binary but not within this module,
// so we allow unused imports for the re-export pattern.
#[allow(unused_imports)]
pub use claims::*;
#[allow(unused_imports)]
pub use corpus::*;
#[allow(unused_imports)]
pub use eval::*;
#[allow(unused_imports)]
pub use extractors::*;
#[allow(unused_imports)]
pub use governance::*;
#[allow(unused_imports)]
pub use lifecycle::*;
#[allow(unused_imports)]
pub use patterns::*;
#[allow(unused_imports)]
pub use policy::*;
#[allow(unused_imports)]
pub use policy_ops::*;
#[allow(unused_imports)]
pub use research::*;
#[allow(unused_imports)]
pub use scan::*;
#[allow(unused_imports)]
pub use scope::*;
#[allow(unused_imports)]
pub use shadow::*;
#[allow(unused_imports)]
pub use utils::*;
#[allow(unused_imports)]
pub use verify::*;

/// Dispatch and execute CLI commands
pub async fn handle_command(command: Commands, config: &AphoriaConfig) -> ExitCode {
    match command {
        Commands::Scan {
            path,
            format,
            exit_code,
            strict,
            persist,
            debug,
            sync,
            staged,
            community_preview,
            benchmark,
            show_claims,
        } => {
            if community_preview {
                scan::handle_community_preview(path, config).await
            } else {
                scan::handle_scan(
                    path,
                    format,
                    exit_code,
                    strict,
                    persist,
                    debug,
                    sync,
                    staged,
                    benchmark,
                    show_claims,
                    config,
                )
                .await
            }
        }

        Commands::Ack { command } => policy_ops::handle_ack_command(command, config).await,

        Commands::Bless { concept_path, predicate, value, reason } => {
            policy_ops::handle_bless(concept_path, predicate, value, reason, config).await
        }

        Commands::Update { concept_path, value, reason } => {
            policy_ops::handle_update(concept_path, value, reason, config).await
        }

        Commands::Baseline => policy_ops::handle_baseline(config).await,

        Commands::Diff => policy_ops::handle_diff(config).await,

        Commands::Status => policy_ops::handle_status(config).await,

        Commands::Init => policy_ops::handle_init(config).await,

        Commands::Corpus { command } => corpus::handle_corpus_command(command, config).await,

        Commands::Research { command } => research::handle_research_command(command, config).await,

        Commands::Policy { command } => policy::handle_policy_command(command, config).await,

        Commands::Extractors { command } => {
            extractors::handle_extractor_command(command, config).await
        }

        Commands::Eval { command } => eval::handle_eval_command(command, config).await,

        Commands::Patterns { command } => patterns::handle_pattern_command(command, config).await,

        Commands::Scope { command } => scope::handle_scope_command(command, config).await,

        Commands::Lifecycle { command } => {
            lifecycle::handle_lifecycle_command(command, config).await
        }

        Commands::Migrations { command } => {
            lifecycle::handle_migrations_command(command, config).await
        }

        Commands::Governance { command } => {
            governance::handle_governance_command(command, config).await
        }

        Commands::Audit { command } => governance::handle_audit_command(command, config).await,

        Commands::Claims { command } => claims::handle_claims_command(command, config).await,

        Commands::Verify { command } => verify::handle_verify_command(command, config).await,

        Commands::Coverage { path, format, sort_by } => {
            let project_root = if path.as_os_str() == "." {
                match std::env::current_dir() {
                    Ok(p) => p,
                    Err(e) => {
                        eprintln!("Cannot determine project root: {e}");
                        return ExitCode::from(1);
                    }
                }
            } else {
                path
            };

            // Load claims and run scan to get observations
            let claims_path = aphoria::claims_file::ClaimsFile::default_path(&project_root);
            let claims_file = match aphoria::claims_file::ClaimsFile::load(&claims_path) {
                Ok(cf) => cf,
                Err(_) => aphoria::claims_file::ClaimsFile::new(),
            };

            let scan_args = aphoria::ScanArgs {
                path: project_root.clone(),
                format: "table".to_string(),
                exit_code_enabled: false,
                mode: aphoria::ScanMode::Ephemeral,
                debug: false,
                sync: false,
                file_source: aphoria::FileSource::All,
                benchmark: false,
                show_claims: true,
                strict: false,
            };

            let observations = match aphoria::run_scan(scan_args, config).await {
                Ok(result) => result.claims.unwrap_or_default(),
                Err(e) => {
                    eprintln!("Scan error: {e}");
                    return ExitCode::from(1);
                }
            };

            let project_name =
                project_root.file_name().and_then(|n| n.to_str()).unwrap_or("project");

            let report =
                aphoria::compute_coverage(&claims_file.claims, &observations, project_name);

            let output = match format.as_str() {
                "json" => aphoria::format_coverage_json(&report),
                "markdown" => aphoria::format_coverage_markdown(&report),
                _ => aphoria::format_coverage_table(&report, &sort_by),
            };
            println!("{output}");
            ExitCode::SUCCESS
        }

        Commands::Explain { path, output, format } => {
            let project_root = if path.as_os_str() == "." {
                match std::env::current_dir() {
                    Ok(p) => p,
                    Err(e) => {
                        eprintln!("Cannot determine project root: {e}");
                        return ExitCode::from(1);
                    }
                }
            } else {
                path
            };

            match gather_explain_data(&project_root, config).await {
                Ok((claims, observations, project_name)) => {
                    let verify_report = aphoria::verify_claims(&claims, &observations);
                    let coverage_report = aphoria::compute_coverage_from_report(
                        &claims,
                        &observations,
                        &verify_report,
                        &project_name,
                    );
                    let text = aphoria::explain::generate_onboarding(
                        &claims,
                        &verify_report,
                        &coverage_report,
                        &project_name,
                        &format,
                    );
                    write_or_print(&text, output.as_deref())
                }
                Err(code) => code,
            }
        }

        Commands::Docs { command } => match command {
            crate::cli::DocsCommands::Generate { path, output, format } => {
                let project_root = if path.as_os_str() == "." {
                    match std::env::current_dir() {
                        Ok(p) => p,
                        Err(e) => {
                            eprintln!("Cannot determine project root: {e}");
                            return ExitCode::from(1);
                        }
                    }
                } else {
                    path
                };

                match gather_explain_data(&project_root, config).await {
                    Ok((claims, observations, project_name)) => {
                        let verify_report = aphoria::verify_claims(&claims, &observations);
                        let coverage_report = aphoria::compute_coverage_from_report(
                            &claims,
                            &observations,
                            &verify_report,
                            &project_name,
                        );
                        let text = aphoria::explain::generate_full_docs(
                            &claims,
                            &verify_report,
                            &coverage_report,
                            &project_name,
                            &format,
                        );
                        write_or_print(&text, output.as_deref())
                    }
                    Err(code) => code,
                }
            }
        },

        Commands::TrustPack { command } => match command {
            crate::cli::TrustPackCommands::Install { name, registry } => {
                if let Some(registry_url) = registry {
                    eprintln!("Custom registry not yet supported: {registry_url}");
                    eprintln!("Use a built-in pack name or omit --registry.");
                    return ExitCode::from(1);
                }
                match aphoria::trust_pack_registry::lookup(&name) {
                    Ok(entry) => {
                        println!("Trust Pack: {}", entry.name);
                        println!("  {}", entry.description);
                        println!("  Tier: {}", entry.tier);
                        println!("  URL: {}", entry.url);
                        println!();
                        println!("Download not yet implemented (requires hosting infrastructure).");
                        println!(
                            "For now, use `aphoria corpus build` to build assertions locally,"
                        );
                        println!("or `aphoria policy import <file>` to import a .pack file.");
                        ExitCode::SUCCESS
                    }
                    Err(e) => {
                        eprintln!("{e}");
                        ExitCode::from(1)
                    }
                }
            }
            crate::cli::TrustPackCommands::List => {
                let packs = aphoria::trust_pack_registry::list_packs();
                println!("Available Trust Packs:");
                println!();
                for pack in packs {
                    println!("  {} ({})", pack.name, pack.tier);
                    println!("    {}", pack.description);
                }
                println!();
                println!("Install with: aphoria trust-pack install <name>");
                ExitCode::SUCCESS
            }
        },
    }
}

/// Gather claims + observations for explain/docs commands.
/// Returns (claims, observations, project_name) or an ExitCode on error.
async fn gather_explain_data(
    project_root: &std::path::Path,
    config: &AphoriaConfig,
) -> Result<(Vec<aphoria::AuthoredClaim>, Vec<aphoria::Observation>, String), ExitCode> {
    let claims_path = aphoria::claims_file::ClaimsFile::default_path(project_root);
    let claims_file = match aphoria::claims_file::ClaimsFile::load(&claims_path) {
        Ok(cf) => cf,
        Err(_) => aphoria::claims_file::ClaimsFile::new(),
    };

    let scan_args = aphoria::ScanArgs {
        path: project_root.to_path_buf(),
        format: "table".to_string(),
        exit_code_enabled: false,
        mode: aphoria::ScanMode::Ephemeral,
        debug: false,
        sync: false,
        file_source: aphoria::FileSource::All,
        benchmark: false,
        show_claims: true,
        strict: false,
    };

    let observations = match aphoria::run_scan(scan_args, config).await {
        Ok(result) => result.claims.unwrap_or_default(),
        Err(e) => {
            eprintln!("Scan error: {e}");
            return Err(ExitCode::from(1));
        }
    };

    let project_name =
        project_root.file_name().and_then(|n| n.to_str()).unwrap_or("project").to_string();

    Ok((claims_file.claims, observations, project_name))
}

/// Write text to a file or print to stdout.
fn write_or_print(text: &str, output: Option<&std::path::Path>) -> ExitCode {
    if let Some(out_path) = output {
        if let Err(e) = std::fs::write(out_path, text) {
            eprintln!("Failed to write to {}: {e}", out_path.display());
            return ExitCode::from(1);
        }
        println!("Written to {}", out_path.display());
    } else {
        println!("{text}");
    }
    ExitCode::SUCCESS
}
