use std::fs;
use std::path::PathBuf;

use codspeed_criterion_compat::{BenchmarkId, Criterion, criterion_group, criterion_main};
use std::hint::black_box;

use sqpack_rs::{Index, Index2};

mod common;

const ENTRY_COUNT: usize = 1000;

struct Fixture {
    root: PathBuf,
    v1_path: PathBuf,
    v2_path: PathBuf,
}

impl Fixture {
    fn new() -> Self {
        let root = common::unique_tempdir("index_load");
        fs::create_dir_all(&root).expect("mkdir bench root");

        let v1_entries: Vec<(u64, u32)> = (0..ENTRY_COUNT)
            .map(|i| {
                let hash = 0x1000_0000_0000_0000_u64 + u64::try_from(i).unwrap();
                let data = u32::try_from(i).unwrap() * 0x10;
                (hash, data)
            })
            .collect();
        let v1_bytes = common::make_index_file(&v1_entries);
        let v1_path = root.join("index_v1.bin");
        fs::write(&v1_path, &v1_bytes).expect("write v1 fixture");

        let v2_entries: Vec<(u32, u32)> = (0..ENTRY_COUNT)
            .map(|i| {
                let hash = 0x1000_0000_u32 + u32::try_from(i).unwrap();
                let data = u32::try_from(i).unwrap() * 0x10;
                (hash, data)
            })
            .collect();
        let v2_bytes = common::make_index2_file(&v2_entries);
        let v2_path = root.join("index_v2.bin");
        fs::write(&v2_path, &v2_bytes).expect("write v2 fixture");

        Fixture {
            root,
            v1_path,
            v2_path,
        }
    }
}

impl Drop for Fixture {
    fn drop(&mut self) {
        let _ = fs::remove_dir_all(&self.root);
    }
}

fn bench_index_open(c: &mut Criterion) {
    let fixture = Fixture::new();
    let mut group = c.benchmark_group("index.open");

    group.bench_with_input(
        BenchmarkId::from_parameter("v1"),
        &fixture.v1_path,
        |b, p| {
            b.iter(|| {
                let idx = Index::open(black_box(p)).expect("v1 fixture must parse");
                black_box(idx);
            });
        },
    );

    group.bench_with_input(
        BenchmarkId::from_parameter("v2"),
        &fixture.v2_path,
        |b, p| {
            b.iter(|| {
                let idx = Index2::open(black_box(p)).expect("v2 fixture must parse");
                black_box(idx);
            });
        },
    );

    group.finish();

    drop(fixture);
}

criterion_group!(benches, bench_index_open);
criterion_main!(benches);
