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::DatFile;

mod common;

struct Fixture {
    root: PathBuf,
    single_path: PathBuf,
    single_offset: u64,
    multi_path: PathBuf,
    multi_offset: u64,
}

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

        let single_payload = common::build_compressible_payload(4096);
        let (single_buf, single_offset) =
            common::make_standard_dat(&[(single_payload.as_slice(), true)]);
        let single_path = root.join("single.dat");
        fs::write(&single_path, &single_buf).expect("write single fixture");

        let a = common::build_compressible_payload(1024);
        let b: Vec<u8> = vec![0xABu8; 1024];
        let c = common::build_compressible_payload(1024);
        let d: Vec<u8> = vec![0xCDu8; 1024];
        let blocks: [(&[u8], bool); 4] = [
            (a.as_slice(), true),
            (b.as_slice(), false),
            (c.as_slice(), true),
            (d.as_slice(), false),
        ];
        let (multi_buf, multi_offset) = common::make_standard_dat(&blocks);
        let multi_path = root.join("multi.dat");
        fs::write(&multi_path, &multi_buf).expect("write multi fixture");

        Fixture {
            root,
            single_path,
            single_offset,
            multi_path,
            multi_offset,
        }
    }
}

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

fn bench_dat_read_file(c: &mut Criterion) {
    let fixture = Fixture::new();
    let mut group = c.benchmark_group("dat.read_file");

    group.bench_with_input(
        BenchmarkId::from_parameter("single_compressed_block"),
        &(&fixture.single_path, fixture.single_offset),
        |b, (path, off)| {
            b.iter(|| {
                let mut dat = DatFile::open(black_box(*path)).expect("open single fixture");
                let bytes = dat.read_file(black_box(*off)).expect("read single block");
                black_box(bytes);
            });
        },
    );

    group.bench_with_input(
        BenchmarkId::from_parameter("multi_block_mixed"),
        &(&fixture.multi_path, fixture.multi_offset),
        |b, (path, off)| {
            b.iter(|| {
                let mut dat = DatFile::open(black_box(*path)).expect("open multi fixture");
                let bytes = dat.read_file(black_box(*off)).expect("read multi block");
                black_box(bytes);
            });
        },
    );

    group.finish();
    drop(fixture);
}

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