//! Criterion benchmarks for tsafe core operations.
//!
//! Run:  cargo bench --package tsafe-core
//! HTML: target/criterion/report/index.html

use criterion::{criterion_group, criterion_main, Criterion};
use std::collections::HashMap;
use tempfile::tempdir;
use tsafe_core::crypto;
use tsafe_core::vault::Vault;

const BENCH_PW: &[u8] = b"benchmark-password-99";

/// Pre-populate a vault with N secrets and return the path.
fn create_vault_with_entries(n: usize) -> (tempfile::TempDir, std::path::PathBuf) {
    let dir = tempdir().unwrap();
    let path = dir.path().join("bench.vault");
    let mut vault = Vault::create(&path, BENCH_PW).unwrap();
    for i in 0..n {
        vault
            .set(
                &format!("KEY_{i:04}"),
                &format!("value-{i}-{}", "x".repeat(50)),
                HashMap::new(),
            )
            .unwrap();
    }
    drop(vault);
    (dir, path)
}

fn bench_kdf(c: &mut Criterion) {
    let salt = crypto::random_salt();
    c.bench_function("argon2id_kdf_64mib", |b| {
        b.iter(|| {
            crypto::derive_key(
                b"benchmark-password",
                &salt,
                crypto::VAULT_KDF_M_COST,
                crypto::VAULT_KDF_T_COST,
                crypto::VAULT_KDF_P_COST,
            )
            .unwrap()
        })
    });
}

fn bench_vault_open(c: &mut Criterion) {
    let (_dir, path) = create_vault_with_entries(100);
    c.bench_function("vault_open_100_entries", |b| {
        b.iter(|| {
            let vault = Vault::open(&path, BENCH_PW).unwrap();
            drop(vault);
        })
    });
}

fn bench_vault_save(c: &mut Criterion) {
    let (_dir, path) = create_vault_with_entries(100);
    c.bench_function("vault_save_100_entries", |b| {
        b.iter(|| {
            let mut vault = Vault::open(&path, BENCH_PW).unwrap();
            vault
                .set("BENCH_KEY", "bench-value", HashMap::new())
                .unwrap();
            drop(vault);
        })
    });
}

fn bench_encrypt_decrypt(c: &mut Criterion) {
    let salt = crypto::random_salt();
    let key = crypto::derive_key(
        b"benchmark-password",
        &salt,
        crypto::VAULT_KDF_M_COST,
        crypto::VAULT_KDF_T_COST,
        crypto::VAULT_KDF_P_COST,
    )
    .unwrap();
    let plaintext = b"a]secret value that is 64 bytes long for realistic benchmarking!";

    c.bench_function("xchacha20_encrypt", |b| {
        b.iter(|| crypto::encrypt(&key, plaintext).unwrap())
    });

    let (nonce, ct) = crypto::encrypt(&key, plaintext).unwrap();
    c.bench_function("xchacha20_decrypt", |b| {
        b.iter(|| crypto::decrypt(&key, &nonce, &ct).unwrap())
    });
}

criterion_group!(
    benches,
    bench_kdf,
    bench_vault_open,
    bench_vault_save,
    bench_encrypt_decrypt
);
criterion_main!(benches);
