//! HNSW Index Benchmarks
//!
//! Run with: cargo bench

use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
use rand::Rng;

use aifastdb_core::{
    types::HNSWConfig,
    vector::HNSWIndex,
};

fn generate_random_vector(dim: usize) -> Vec<f32> {
    let mut rng = rand::thread_rng();
    (0..dim).map(|_| rng.gen::<f32>()).collect()
}

fn generate_random_vectors(count: usize, dim: usize) -> Vec<Vec<f32>> {
    (0..count).map(|_| generate_random_vector(dim)).collect()
}

fn bench_insert(c: &mut Criterion) {
    let mut group = c.benchmark_group("hnsw_insert");
    
    for size in [1000, 10000].iter() {
        let config = HNSWConfig::new(128)
            .with_m(16)
            .with_ef_construction(200)
            .with_max_elements(*size + 1000);
        
        let vectors = generate_random_vectors(*size, 128);
        
        group.bench_with_input(
            BenchmarkId::new("insert", size),
            size,
            |b, &size| {
                b.iter(|| {
                    let index = HNSWIndex::new(config.clone());
                    for (i, vec) in vectors.iter().take(size).enumerate() {
                        let _ = index.insert(format!("v{}", i), vec.clone());
                    }
                    black_box(index.len())
                });
            },
        );
    }
    
    group.finish();
}

fn bench_search(c: &mut Criterion) {
    let mut group = c.benchmark_group("hnsw_search");
    
    for size in [1000, 10000, 50000].iter() {
        let config = HNSWConfig::new(128)
            .with_m(16)
            .with_ef_construction(200)
            .with_ef_search(50)
            .with_max_elements(*size + 100);
        
        let index = HNSWIndex::new(config);
        let vectors = generate_random_vectors(*size, 128);
        
        // Build index
        for (i, vec) in vectors.iter().enumerate() {
            let _ = index.insert(format!("v{}", i), vec.clone());
        }
        
        let query = generate_random_vector(128);
        
        group.bench_with_input(
            BenchmarkId::new("search_top10", size),
            size,
            |b, _| {
                b.iter(|| {
                    let results = index.search(black_box(&query), 10).unwrap();
                    black_box(results.len())
                });
            },
        );
    }
    
    group.finish();
}

fn bench_ef_search(c: &mut Criterion) {
    let mut group = c.benchmark_group("hnsw_ef_search");
    
    let size = 10000;
    let dim = 128;
    
    let config = HNSWConfig::new(dim)
        .with_m(16)
        .with_ef_construction(200)
        .with_max_elements(size + 100);
    
    let index = HNSWIndex::new(config);
    let vectors = generate_random_vectors(size, dim);
    
    for (i, vec) in vectors.iter().enumerate() {
        let _ = index.insert(format!("v{}", i), vec.clone());
    }
    
    let query = generate_random_vector(dim);
    
    for ef in [10, 50, 100, 200].iter() {
        group.bench_with_input(
            BenchmarkId::new("ef", ef),
            ef,
            |b, _| {
                // Note: ef_search is set during index creation
                // This benchmark shows relative performance
                b.iter(|| {
                    let results = index.search(black_box(&query), 10).unwrap();
                    black_box(results.len())
                });
            },
        );
    }
    
    group.finish();
}

fn bench_dimensions(c: &mut Criterion) {
    let mut group = c.benchmark_group("hnsw_dimensions");
    
    let size = 5000;
    
    for dim in [64, 128, 256, 512, 1536].iter() {
        let config = HNSWConfig::new(*dim)
            .with_m(16)
            .with_ef_construction(100)
            .with_ef_search(50)
            .with_max_elements(size + 100);
        
        let index = HNSWIndex::new(config);
        let vectors = generate_random_vectors(size, *dim);
        
        for (i, vec) in vectors.iter().enumerate() {
            let _ = index.insert(format!("v{}", i), vec.clone());
        }
        
        let query = generate_random_vector(*dim);
        
        group.bench_with_input(
            BenchmarkId::new("dim", dim),
            dim,
            |b, _| {
                b.iter(|| {
                    let results = index.search(black_box(&query), 10).unwrap();
                    black_box(results.len())
                });
            },
        );
    }
    
    group.finish();
}

criterion_group!(
    benches,
    bench_insert,
    bench_search,
    bench_ef_search,
    bench_dimensions,
);

criterion_main!(benches);
