use std::path::PathBuf;
use std::time::Instant;

use clap::Parser;

use kindi::detect::{scan_package, ScanConfig};
use kindi::output::CryptoOutput;
use kindi::pattern::PatternDb;
use kindi::toon;

#[derive(Parser)]
#[command(
    name = "kindi",
    about = "Cryptographic evidence hunter. Named for Al-Kindi (801-873 CE), father of cryptanalysis.",
    version
)]
struct Cli {
    /// Packages or directories to scan
    #[arg(required = true)]
    packages: Vec<PathBuf>,

    /// Path to keyword list file
    #[arg(long, default_value = "keyword_list.txt")]
    keywords: PathBuf,

    /// Path to API definitions file (optional)
    #[arg(long)]
    api_defs: Option<PathBuf>,

    /// Case-insensitive keyword matching
    #[arg(long, short = 'i')]
    ignore_case: bool,

    /// Quick scan: just report files with matches, no details
    #[arg(long, short)]
    quick: bool,

    /// Only scan source code files (skip binary, config, docs)
    #[arg(long)]
    source_only: bool,

    /// Also scan binary files for crypto evidence
    #[arg(long)]
    scan_binary: bool,

    /// Stop scanning after N files with matches
    #[arg(long, short = 's')]
    stop_after: Option<usize>,

    /// Evidence types to ignore (comma-separated)
    #[arg(long, value_delimiter = ',')]
    ignore_evidence_types: Vec<String>,

    /// Output directory for JSON results
    #[arg(long, short, default_value = ".")]
    output: PathBuf,

    /// Also write CSV output alongside JSON
    #[arg(long)]
    csv: bool,

    /// Token-optimized output for AI agent consumption (~99% fewer tokens than JSON)
    #[arg(long)]
    toon: bool,

    /// Pretty-print JSON output
    #[arg(long)]
    pretty: bool,

    /// Verbose output
    #[arg(long, short)]
    verbose: bool,
}

fn main() {
    let cli = Cli::parse();
    let start = Instant::now();

    // Load pattern databases
    let keyword_db = match PatternDb::from_file(&cli.keywords, cli.ignore_case) {
        Ok(db) => {
            if cli.verbose {
                eprintln!(
                    "Loaded {} keyword patterns (v{})",
                    db.pattern_count(),
                    db.version()
                );
            }
            db
        }
        Err(e) => {
            eprintln!("Error loading keyword list: {e}");
            std::process::exit(1);
        }
    };

    // Load API definitions with whole-word matching
    let api_db = cli.api_defs.as_ref().map(|path| {
        match PatternDb::from_api_file(path, cli.ignore_case, &cli.ignore_evidence_types) {
            Ok(db) => {
                if cli.verbose {
                    eprintln!("Loaded {} API patterns (v{})", db.pattern_count(), db.version());
                }
                db
            }
            Err(e) => {
                eprintln!("Error loading API definitions: {e}");
                std::process::exit(1);
            }
        }
    });

    let config = ScanConfig {
        keyword_db,
        api_db,
        source_files_only: cli.source_only,
        quick: cli.quick,
        stop_after: cli.stop_after,
        scan_binary: cli.scan_binary,
    };

    let mut total_files = 0usize;
    let mut total_matches = 0usize;

    for package in &cli.packages {
        if !package.exists() {
            eprintln!("Package not found: {}", package.display());
            continue;
        }

        if cli.verbose {
            eprintln!("Scanning package: {}", package.display());
        }

        match scan_package(package, &config) {
            Ok(result) => {
                total_files += result.files_scanned;
                total_matches += result.total_matches;
                let elapsed = start.elapsed().as_secs_f64();

                // Toon output (AI agent mode)
                if cli.toon {
                    toon::print_toon(&result, elapsed, 20);
                    continue;
                }

                // JSON output
                let output = CryptoOutput::from_scan(&result);
                let out_path = cli.output.join(format!("{}.crypto", result.package_name));

                if let Err(e) = output.write_json(&out_path, cli.pretty) {
                    eprintln!("Error writing output: {e}");
                } else if cli.verbose {
                    eprintln!(
                        "  {} files scanned, {} with matches, {} total hits -> {}",
                        result.files_scanned,
                        result.files_with_matches,
                        result.total_matches,
                        out_path.display()
                    );
                }

                // CSV output
                if cli.csv {
                    let csv_path = cli.output.join(format!("{}.csv", result.package_name));
                    if let Err(e) = output.write_csv(&csv_path) {
                        eprintln!("Error writing CSV: {e}");
                    } else if cli.verbose {
                        eprintln!("  CSV -> {}", csv_path.display());
                    }
                }
            }
            Err(e) => {
                eprintln!("Error scanning {}: {e}", package.display());
            }
        }
    }

    if !cli.toon {
        let elapsed = start.elapsed();
        eprintln!(
            "Scanned {} files, found {} matches in {:.3}s",
            total_files,
            total_matches,
            elapsed.as_secs_f64()
        );
    }
}
