//! Comprehensive benchmarks for kHeavyHash mining.
//!
//! Benchmarks:
//! - GF(2^8) multiplication tables
//! - Matrix multiplication (standard vs optimized)
//! - kHeavyHash stages (pre-hash, finalize, full)
//! - Mining throughput (single and parallel)
//! - Difficulty validation
//!
//! Run with: cargo bench -p synor-mining

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use synor_mining::kheavyhash::{HashrateBenchmark, KHeavyHash, ParallelMiner};
use synor_mining::matrix::{gf_mul, HeavyMatrix, OptimizedMatrix};
use synor_mining::Target;

// ==================== GF(2^8) Operations ====================

fn bench_gf_mul(c: &mut Criterion) {
    c.bench_function("gf_mul_full_table", |b| {
        b.iter(|| {
            for a in 0..=255u8 {
                for bb in 0..=255u8 {
                    black_box(gf_mul(a, bb));
                }
            }
        })
    });
}

fn bench_gf_mul_single(c: &mut Criterion) {
    c.bench_function("gf_mul_single", |b| {
        let a = 0x53u8;
        let bb = 0xCAu8;
        b.iter(|| black_box(gf_mul(a, bb)))
    });
}

// ==================== Matrix Operations ====================

fn bench_matrix_multiply(c: &mut Criterion) {
    let matrix = HeavyMatrix::new();
    let input = [0x42u8; 32];

    c.bench_function("matrix_multiply_standard", |b| {
        b.iter(|| black_box(matrix.multiply(&input)))
    });
}

fn bench_optimized_matrix_multiply(c: &mut Criterion) {
    let matrix = OptimizedMatrix::new();
    let input = [0x42u8; 32];

    c.bench_function("matrix_multiply_optimized", |b| {
        b.iter(|| black_box(matrix.multiply(&input)))
    });
}

fn bench_matrix_creation(c: &mut Criterion) {
    c.bench_function("matrix_create_optimized", |b| {
        b.iter(|| black_box(OptimizedMatrix::new()))
    });
}

fn bench_matrix_from_seed(c: &mut Criterion) {
    let seed = [0x42u8; 32];
    c.bench_function("matrix_from_seed", |b| {
        b.iter(|| black_box(OptimizedMatrix::from_seed(&seed)))
    });
}

// ==================== kHeavyHash Core ====================

fn bench_kheavyhash(c: &mut Criterion) {
    let hasher = KHeavyHash::new();
    let header = [0x42u8; 80];

    let mut group = c.benchmark_group("kheavyhash");
    group.throughput(Throughput::Elements(1));

    // Full hash (header + nonce -> pow_hash)
    group.bench_function("full_hash", |b| {
        let mut nonce = 0u64;
        b.iter(|| {
            let result = hasher.hash(&header, nonce);
            nonce = nonce.wrapping_add(1);
            black_box(result)
        })
    });

    // Pre-hash only (SHA3-256 of header)
    group.bench_function("pre_hash_only", |b| {
        b.iter(|| black_box(hasher.pre_hash(&header)))
    });

    // Finalize only (Blake3 + matrix + SHA3 per nonce)
    let pre_hash = hasher.pre_hash(&header);
    group.bench_function("finalize_only", |b| {
        let mut nonce = 0u64;
        b.iter(|| {
            let result = hasher.finalize(&pre_hash, nonce);
            nonce = nonce.wrapping_add(1);
            black_box(result)
        })
    });

    group.finish();
}

fn bench_kheavyhash_varying_headers(c: &mut Criterion) {
    let hasher = KHeavyHash::new();

    let mut group = c.benchmark_group("kheavyhash_header_size");

    for size in [32, 64, 80, 128, 256] {
        let header = vec![0x42u8; size];
        group.throughput(Throughput::Bytes(size as u64));
        group.bench_with_input(BenchmarkId::from_parameter(size), &header, |b, h| {
            let mut nonce = 0u64;
            b.iter(|| {
                let result = hasher.hash(h, nonce);
                nonce = nonce.wrapping_add(1);
                black_box(result)
            })
        });
    }

    group.finish();
}

// ==================== Mining Throughput ====================

fn bench_mining_throughput(c: &mut Criterion) {
    let hasher = KHeavyHash::new();
    let header = [0x42u8; 80];
    let pre_hash = hasher.pre_hash(&header);

    let mut group = c.benchmark_group("mining_throughput");

    for count in [100, 1000, 10000] {
        group.throughput(Throughput::Elements(count));
        group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &count| {
            b.iter(|| {
                for nonce in 0..count {
                    black_box(hasher.finalize(&pre_hash, nonce));
                }
            })
        });
    }

    group.finish();
}

fn bench_mining_with_target(c: &mut Criterion) {
    let hasher = KHeavyHash::new();
    let header = [0x42u8; 80];
    let target = Target::max(); // Easy target for benchmarking

    let mut group = c.benchmark_group("mining_with_target");
    group.throughput(Throughput::Elements(1));

    group.bench_function("mine_easy_target", |b| {
        b.iter_batched(
            || 0u64,
            |start_nonce| black_box(hasher.mine(&header, &target, start_nonce, 10000)),
            criterion::BatchSize::SmallInput,
        )
    });

    group.finish();
}

// ==================== Target Validation ====================

fn bench_target_validation(c: &mut Criterion) {
    let hasher = KHeavyHash::new();
    let header = [0x42u8; 80];
    let pre_hash = hasher.pre_hash(&header);
    let pow = hasher.finalize(&pre_hash, 12345);

    let target = Target::max();

    c.bench_function("target_is_met_by", |b| {
        b.iter(|| black_box(target.is_met_by(&pow.hash)))
    });
}

fn bench_pow_meets_target(c: &mut Criterion) {
    let hasher = KHeavyHash::new();
    let header = [0x42u8; 80];
    let target = Target::max();

    // Find a valid nonce first
    let pow = hasher.mine(&header, &target, 0, 10000).unwrap();

    c.bench_function("pow_meets_target", |b| {
        b.iter(|| black_box(pow.meets_target(&target)))
    });
}

// ==================== Parallel Mining ====================

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

    for threads in [1, 2, 4, 8] {
        group.bench_with_input(BenchmarkId::from_parameter(threads), &threads, |b, &t| {
            b.iter(|| black_box(ParallelMiner::new(t)))
        });
    }

    group.finish();
}

fn bench_hashrate_measurement(c: &mut Criterion) {
    c.bench_function("hashrate_benchmark_100ms", |b| {
        let bench = HashrateBenchmark::new();
        b.iter(|| black_box(bench.run(100)))
    });
}

// ==================== Hasher Creation ====================

fn bench_hasher_creation(c: &mut Criterion) {
    c.bench_function("kheavyhash_new", |b| {
        b.iter(|| black_box(KHeavyHash::new()))
    });

    c.bench_function("kheavyhash_with_seed", |b| {
        let seed = [0x42u8; 32];
        b.iter(|| black_box(KHeavyHash::with_seed(&seed)))
    });
}

// ==================== Verification ====================

fn bench_verify(c: &mut Criterion) {
    let hasher = KHeavyHash::new();
    let header = [0x42u8; 80];
    let target = Target::max();

    // Find a valid nonce
    let pow = hasher.mine(&header, &target, 0, 10000).unwrap();

    c.bench_function("verify_pow", |b| {
        b.iter(|| black_box(hasher.verify(&header, pow.nonce, &target)))
    });
}

// ==================== Memory Comparison ====================

fn bench_matrix_memory(c: &mut Criterion) {
    // This measures the cost of matrix operations with different input patterns
    let matrix = OptimizedMatrix::new();

    let mut group = c.benchmark_group("matrix_input_patterns");

    // Zero input
    let zero_input = [0u8; 32];
    group.bench_function("zeros", |b| {
        b.iter(|| black_box(matrix.multiply(&zero_input)))
    });

    // All ones
    let ones_input = [0xFFu8; 32];
    group.bench_function("ones", |b| {
        b.iter(|| black_box(matrix.multiply(&ones_input)))
    });

    // Random pattern
    let random_input: [u8; 32] = {
        let mut arr = [0u8; 32];
        for (i, byte) in arr.iter_mut().enumerate() {
            *byte = (i as u8).wrapping_mul(17).wrapping_add(31);
        }
        arr
    };
    group.bench_function("random", |b| {
        b.iter(|| black_box(matrix.multiply(&random_input)))
    });

    group.finish();
}

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

criterion_group!(gf_benches, bench_gf_mul, bench_gf_mul_single,);

criterion_group!(
    matrix_benches,
    bench_matrix_multiply,
    bench_optimized_matrix_multiply,
    bench_matrix_creation,
    bench_matrix_from_seed,
    bench_matrix_memory,
);

criterion_group!(
    kheavyhash_benches,
    bench_kheavyhash,
    bench_kheavyhash_varying_headers,
    bench_hasher_creation,
);

criterion_group!(
    mining_benches,
    bench_mining_throughput,
    bench_mining_with_target,
    bench_hashrate_measurement,
);

criterion_group!(
    validation_benches,
    bench_target_validation,
    bench_pow_meets_target,
    bench_verify,
);

criterion_group!(parallel_benches, bench_parallel_miner_creation,);

criterion_main!(
    gf_benches,
    matrix_benches,
    kheavyhash_benches,
    mining_benches,
    validation_benches,
    parallel_benches
);
