mod audit;
mod cli;
mod commands;
mod config;
mod crypto;
mod error;
mod keychain;
mod mask;
mod parser;
mod permissions;
mod provider;
mod store;

use clap::{CommandFactory, Parser};

use cli::{Cli, Command};
use config::Config;
use provider::cache::ProviderCache;
use provider::ProviderRegistry;

fn init_providers(config: &Config, profile: &Option<String>) -> (ProviderRegistry, ProviderCache) {
    let registry = match ProviderRegistry::from_config(config.effective_providers(profile)) {
        Ok(r) => r,
        Err(e) => {
            eprintln!("s2: provider init error: {}", e);
            std::process::exit(1);
        }
    };

    let cache = match ProviderCache::load() {
        Ok(c) => c,
        Err(e) => {
            eprintln!("s2: warning: could not load provider cache: {}", e);
            ProviderCache::default()
        }
    };

    (registry, cache)
}

fn main() {
    let cli = Cli::parse();
    let config = match Config::load() {
        Ok(c) => c,
        Err(e) => {
            eprintln!("s2: config error: {}", e);
            std::process::exit(1);
        }
    };

    let result = match cli.command {
        Command::Exec {
            files,
            keys,
            profile,
            clean_env,
            cmd,
        } => {
            let (registry, cache) = init_providers(&config, &profile);
            commands::exec::run(
                &config, registry, cache, files, keys, profile, clean_env, cmd,
            )
        }

        Command::List { files, profile } => {
            let (registry, cache) = init_providers(&config, &profile);
            commands::list::run(&config, registry, cache, files, profile)
        }

        Command::Check {
            keys,
            files,
            profile,
        } => {
            let (registry, cache) = init_providers(&config, &profile);
            commands::check::run(&config, registry, cache, keys, files, profile)
        }

        Command::Init { path, no_encrypt } => commands::init::run(&config, path, no_encrypt),

        Command::Set { key, file } => commands::set::run(&config, key, file),

        Command::Migrate { source, file, ssm } => {
            commands::migrate::run(&config, source, file, ssm)
        }

        Command::Unset { key, file } => commands::unset::run(&config, key, file),

        Command::Encrypt { path } => commands::encrypt::run(&config, path),

        Command::Decrypt { path } => commands::decrypt::run(&config, path),

        Command::Edit { path } => commands::edit::run(&config, path),

        Command::Redact { files, profile } => {
            let (registry, cache) = init_providers(&config, &profile);
            commands::redact::run(&config, registry, cache, files, profile)
        }

        Command::Scan {
            paths,
            staged,
            no_ignore,
            json,
            entropy,
            learn,
            allow,
            allow_with_context,
            list_rules,
        } => commands::scan::run(
            &config,
            paths,
            staged,
            no_ignore,
            json,
            entropy,
            learn,
            allow,
            allow_with_context,
            list_rules,
        ),

        Command::Completions { shell } => {
            clap_complete::generate(shell, &mut Cli::command(), "s2", &mut std::io::stdout());
            Ok(())
        }

        Command::Hook { format } => commands::hook::run(&config, &format),
    };

    if let Err(e) = result {
        eprintln!("s2: {}", e);
        std::process::exit(1);
    }
}
