//! Cryptography benchmarks for Synor.
//!
//! Benchmarks Ed25519, Dilithium3, and hybrid signature operations.
//! Run with: cargo bench -p synor-crypto

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use synor_crypto::keypair::{Dilithium3Keypair, Ed25519Keypair, HybridKeypair};

/// Message sizes to benchmark (in bytes).
const MESSAGE_SIZES: &[usize] = &[32, 256, 1024, 4096, 16384];

// ==================== Ed25519 Benchmarks ====================

fn ed25519_keygen_benchmark(c: &mut Criterion) {
    c.bench_function("ed25519_keygen", |b| {
        b.iter(|| black_box(Ed25519Keypair::generate()))
    });
}

fn ed25519_sign_benchmark(c: &mut Criterion) {
    let keypair = Ed25519Keypair::generate();
    let message = vec![0u8; 256];

    c.bench_function("ed25519_sign_256b", |b| {
        b.iter(|| black_box(keypair.sign(&message)))
    });
}

fn ed25519_sign_varying_sizes(c: &mut Criterion) {
    let keypair = Ed25519Keypair::generate();

    let mut group = c.benchmark_group("ed25519_sign");
    for &size in MESSAGE_SIZES {
        let message = vec![0xABu8; size];
        group.throughput(Throughput::Bytes(size as u64));
        group.bench_with_input(BenchmarkId::from_parameter(size), &message, |b, msg| {
            b.iter(|| black_box(keypair.sign(msg)))
        });
    }
    group.finish();
}

fn ed25519_verify_benchmark(c: &mut Criterion) {
    use ed25519_dalek::Verifier;

    let keypair = Ed25519Keypair::generate();
    let message = vec![0u8; 256];
    let signature = keypair.sign(&message);
    let verifying_key = keypair.verifying_key();

    c.bench_function("ed25519_verify_256b", |b| {
        b.iter(|| black_box(verifying_key.verify(&message, &signature).is_ok()))
    });
}

// ==================== Dilithium3 Benchmarks ====================

fn dilithium_keygen_benchmark(c: &mut Criterion) {
    c.bench_function("dilithium3_keygen", |b| {
        b.iter(|| black_box(Dilithium3Keypair::generate()))
    });
}

fn dilithium_sign_benchmark(c: &mut Criterion) {
    let keypair = Dilithium3Keypair::generate();
    let message = vec![0u8; 256];

    c.bench_function("dilithium3_sign_256b", |b| {
        b.iter(|| black_box(keypair.sign(&message)))
    });
}

fn dilithium_sign_varying_sizes(c: &mut Criterion) {
    let keypair = Dilithium3Keypair::generate();

    let mut group = c.benchmark_group("dilithium3_sign");
    for &size in MESSAGE_SIZES {
        let message = vec![0xABu8; size];
        group.throughput(Throughput::Bytes(size as u64));
        group.bench_with_input(BenchmarkId::from_parameter(size), &message, |b, msg| {
            b.iter(|| black_box(keypair.sign(msg)))
        });
    }
    group.finish();
}

fn dilithium_verify_benchmark(c: &mut Criterion) {
    use pqcrypto_dilithium::dilithium3;

    let keypair = Dilithium3Keypair::generate();
    let message = vec![0u8; 256];
    let signed_message = keypair.sign(&message);
    let public_key = keypair.public_key();

    c.bench_function("dilithium3_verify_256b", |b| {
        b.iter(|| black_box(dilithium3::open(&signed_message, public_key).is_ok()))
    });
}

// ==================== Hybrid Signature Benchmarks ====================

fn hybrid_keygen_benchmark(c: &mut Criterion) {
    c.bench_function("hybrid_keygen", |b| {
        b.iter(|| black_box(HybridKeypair::generate()))
    });
}

fn hybrid_sign_benchmark(c: &mut Criterion) {
    let keypair = HybridKeypair::generate();
    let message = vec![0u8; 256];

    c.bench_function("hybrid_sign_256b", |b| {
        b.iter(|| black_box(keypair.sign(&message)))
    });
}

fn hybrid_sign_varying_sizes(c: &mut Criterion) {
    let keypair = HybridKeypair::generate();

    let mut group = c.benchmark_group("hybrid_sign");
    for &size in MESSAGE_SIZES {
        let message = vec![0xABu8; size];
        group.throughput(Throughput::Bytes(size as u64));
        group.bench_with_input(BenchmarkId::from_parameter(size), &message, |b, msg| {
            b.iter(|| black_box(keypair.sign(msg)))
        });
    }
    group.finish();
}

fn hybrid_verify_benchmark(c: &mut Criterion) {
    let keypair = HybridKeypair::generate();
    let message = vec![0u8; 256];
    let signature = keypair.sign(&message);
    let public_key = keypair.public_key();

    c.bench_function("hybrid_verify_256b", |b| {
        b.iter(|| black_box(public_key.verify(&message, &signature).is_ok()))
    });
}

// ==================== Hashing Benchmarks ====================

fn blake3_hash_benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("blake3_hash");

    for &size in MESSAGE_SIZES {
        let data = vec![0xABu8; size];
        group.throughput(Throughput::Bytes(size as u64));
        group.bench_with_input(BenchmarkId::from_parameter(size), &data, |b, d| {
            b.iter(|| black_box(blake3::hash(d)))
        });
    }
    group.finish();
}

fn blake3_hash_large(c: &mut Criterion) {
    // 1MB benchmark for throughput measurement
    let data = vec![0xABu8; 1024 * 1024];

    c.bench_function("blake3_hash_1mb", |b| {
        b.iter(|| black_box(blake3::hash(&data)))
    });
}

// ==================== Address Derivation Benchmarks ====================

fn address_derivation_benchmark(c: &mut Criterion) {
    use synor_crypto::Network;

    let keypair = HybridKeypair::generate();

    c.bench_function("address_derivation", |b| {
        b.iter(|| black_box(keypair.address(Network::Mainnet)))
    });
}

// ==================== Signature Size Comparison ====================

fn signature_sizes(c: &mut Criterion) {
    // This isn't really a benchmark, but it's useful to document sizes
    let ed_keypair = Ed25519Keypair::generate();
    let dil_keypair = Dilithium3Keypair::generate();
    let hybrid_keypair = HybridKeypair::generate();

    let message = b"test";

    let ed_sig = ed_keypair.sign(message);
    let dil_sig = dil_keypair.sign(message);
    let hybrid_sig = hybrid_keypair.sign(message);

    println!("\n=== Signature Sizes ===");
    println!("Ed25519:    {} bytes", std::mem::size_of_val(&ed_sig));
    println!(
        "Dilithium3: {} bytes",
        pqcrypto_traits::sign::SignedMessage::as_bytes(&dil_sig).len() - message.len()
    );
    println!(
        "Hybrid:     {} bytes",
        hybrid_sig.ed25519_signature.len() + hybrid_sig.dilithium_signature.len()
    );
    println!();

    // Quick sanity check benchmark
    c.bench_function("signature_size_overhead", |b| {
        b.iter(|| {
            let sig = hybrid_keypair.sign(message);
            black_box(sig.ed25519_signature.len() + sig.dilithium_signature.len())
        })
    });
}

// ==================== Criterion Groups ====================

criterion_group!(
    keygen_benches,
    ed25519_keygen_benchmark,
    dilithium_keygen_benchmark,
    hybrid_keygen_benchmark,
);

criterion_group!(
    sign_benches,
    ed25519_sign_benchmark,
    ed25519_sign_varying_sizes,
    dilithium_sign_benchmark,
    dilithium_sign_varying_sizes,
    hybrid_sign_benchmark,
    hybrid_sign_varying_sizes,
);

criterion_group!(
    verify_benches,
    ed25519_verify_benchmark,
    dilithium_verify_benchmark,
    hybrid_verify_benchmark,
);

criterion_group!(hash_benches, blake3_hash_benchmark, blake3_hash_large,);

criterion_group!(misc_benches, address_derivation_benchmark, signature_sizes,);

criterion_main!(
    keygen_benches,
    sign_benches,
    verify_benches,
    hash_benches,
    misc_benches
);
