//! Scan command handlers

use std::process::ExitCode;

use aphoria::{extract_claims, report, run_scan, AphoriaConfig, FileSource, ScanArgs, ScanMode};

#[allow(clippy::too_many_arguments)]
pub async fn handle_scan(
    path: std::path::PathBuf,
    format: String,
    exit_code: bool,
    strict: bool,
    persist: bool,
    debug: bool,
    sync: bool,
    staged: bool,
    benchmark: bool,
    show_claims: bool,
    config: &AphoriaConfig,
) -> ExitCode {
    // Validate: --sync requires --persist
    if sync && !persist {
        eprintln!("Error: --sync requires --persist");
        eprintln!("  Observation write-back needs persistent storage.");
        eprintln!("  Use: aphoria scan --persist --sync");
        return ExitCode::from(3);
    }

    let mode = if persist { ScanMode::Persistent } else { ScanMode::Ephemeral };
    let file_source = if staged { FileSource::Staged } else { FileSource::All };
    let args = ScanArgs {
        path,
        format,
        exit_code_enabled: exit_code,
        mode,
        debug,
        sync,
        file_source,
        benchmark,
        show_claims,
    };

    // Apply stricter thresholds if requested
    let config = if strict {
        let mut strict_config = config.clone();
        strict_config.thresholds.block = 0.5;
        strict_config.thresholds.flag = 0.3;
        strict_config
    } else {
        config.clone()
    };

    match run_scan(args, &config).await {
        Ok(result) => {
            let formatter = report::get_formatter(&result.format);
            println!("{}", formatter.format(&result));

            if exit_code && result.has_blocks() {
                ExitCode::from(2)
            } else if exit_code && (result.has_flags() || result.has_drifts()) {
                ExitCode::from(1)
            } else {
                ExitCode::SUCCESS
            }
        }
        Err(e) => {
            eprintln!("Scan error: {e}");
            ExitCode::from(3)
        }
    }
}

pub async fn handle_community_preview(
    path: std::path::PathBuf,
    config: &AphoriaConfig,
) -> ExitCode {
    use aphoria::community::{anonymize_claim, CommunityObjectValue};

    // Check if community sharing is configured
    if !config.community.is_enabled() {
        eprintln!("Community sharing is not enabled.");
        eprintln!();
        eprintln!("To enable community sharing, add this to your aphoria.toml:");
        eprintln!();
        eprintln!("  [community]");
        eprintln!("  enabled = true");
        eprintln!("  anonymize = true  # Privacy-preserving by default");
        eprintln!("  min_confidence = 0.8");
        eprintln!();
        eprintln!("Community preview shows what WOULD be shared, without sending any data.");
        return ExitCode::from(1);
    }

    // Run a quick ephemeral scan to extract claims
    let args = ScanArgs {
        path: path.clone(),
        format: "table".to_string(),
        exit_code_enabled: false,
        mode: ScanMode::Ephemeral,
        debug: false,
        sync: false,
        file_source: FileSource::All,
        benchmark: false,
        show_claims: false,
    };

    let claims = match extract_claims(&args, config).await {
        Ok(c) => c,
        Err(e) => {
            eprintln!("Error extracting claims: {e}");
            return ExitCode::from(3);
        }
    };

    if claims.is_empty() {
        println!("No claims extracted from this project.");
        return ExitCode::SUCCESS;
    }

    // Get current timestamp
    let timestamp = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);

    // Anonymize claims
    let anonymized: Vec<_> = claims
        .iter()
        .filter_map(|claim| anonymize_claim(claim, &config.community, timestamp))
        .collect();

    // Print preview
    println!("--- Community Preview (what would be shared) ---");
    println!();

    if anonymized.is_empty() {
        println!("No observations would be shared.");
        println!();
        println!("Reasons observations might be excluded:");
        println!(
            "  - Confidence below threshold ({:.0}%)",
            config.community.min_confidence * 100.0
        );
        if !config.community.exclude.is_empty() {
            println!("  - Excluded patterns: {:?}", config.community.exclude);
        }
        if !config.community.include.is_empty() {
            println!("  - Include whitelist: {:?}", config.community.include);
        }
        return ExitCode::SUCCESS;
    }

    println!("Would share {} anonymized observations:", anonymized.len());
    println!();

    // Group by subject prefix for better readability
    for (shown, obs) in anonymized.iter().enumerate() {
        if shown >= 20 {
            println!("  ... and {} more", anonymized.len() - 20);
            break;
        }

        let value_display = match &obs.object {
            CommunityObjectValue::Boolean(b) => b.to_string(),
            CommunityObjectValue::Number(n) => n.to_string(),
            CommunityObjectValue::Text(s) => {
                if s.len() > 20 {
                    format!("\"{}...\"", &s[..20])
                } else {
                    format!("\"{}\"", s)
                }
            }
        };

        println!("  {} :: {} = {}", obs.subject, obs.predicate, value_display);
    }

    println!();
    println!("Privacy guarantees:");
    println!("  - Project names wildcarded: myapp → *");
    println!("  - File paths NOT included");
    println!("  - Line numbers NOT included");
    println!("  - Source code snippets NOT included");
    println!("  - Timestamps rounded to hour");
    println!();
    println!("To actually share with the community, run:");
    println!("  aphoria scan --persist --sync");

    ExitCode::SUCCESS
}
