use std::hint::black_box;

use codspeed_criterion_compat::{Criterion, criterion_group, criterion_main};

use sqpack_rs::pap::Pap;

const PAP_MAGIC: [u8; 4] = *b"pap ";
const PAP_VERSION_V1: u32 = 0x0002_0001;
const HEADER_BYTES: usize = 26;
const INFO_RECORD_BYTES: usize = 40;
const ANIMATION_COUNT: u16 = 4;
const HAVOK_PAYLOAD_BYTES: usize = 4096;
const TMB_FOOTER_BYTES: usize = 16;

fn build_4k_pap() -> Vec<u8> {
    let info_offset = u32::try_from(HEADER_BYTES).expect("26 fits u32");
    let info_size =
        u32::from(ANIMATION_COUNT) * u32::try_from(INFO_RECORD_BYTES).expect("40 fits u32");
    let havok_offset = info_offset + info_size;
    let tmb_offset = havok_offset + u32::try_from(HAVOK_PAYLOAD_BYTES).expect("4096 fits u32");
    let file_size = usize::try_from(tmb_offset).expect("fits") + TMB_FOOTER_BYTES;

    let mut buf = Vec::with_capacity(file_size);
    buf.extend_from_slice(&PAP_MAGIC);
    buf.extend_from_slice(&PAP_VERSION_V1.to_le_bytes());
    buf.extend_from_slice(&ANIMATION_COUNT.to_le_bytes());
    buf.extend_from_slice(&0x0101u16.to_le_bytes());
    buf.push(0x00);
    buf.push(0x01);
    buf.extend_from_slice(&info_offset.to_le_bytes());
    buf.extend_from_slice(&havok_offset.to_le_bytes());
    buf.extend_from_slice(&tmb_offset.to_le_bytes());
    assert_eq!(buf.len(), HEADER_BYTES);

    for i in 0..ANIMATION_COUNT {
        let mut name = [0u8; 32];
        let label = format!("anim_{i:02}");
        let bytes = label.as_bytes();
        let n = bytes.len().min(32);
        name[..n].copy_from_slice(&bytes[..n]);
        buf.extend_from_slice(&name);
        buf.extend_from_slice(&0x0001u16.to_le_bytes());
        buf.extend_from_slice(&i16::try_from(i).unwrap_or(0).to_le_bytes());
        buf.extend_from_slice(&0u32.to_le_bytes());
    }

    buf.extend_from_slice(&0xCAB0_0D1Eu32.to_le_bytes());
    buf.resize(usize::try_from(tmb_offset).expect("fits"), 0);

    buf.extend_from_slice(b"TMLB");
    buf.resize(file_size, 0);

    buf
}

fn bench_pap_parse(c: &mut Criterion) {
    let pap_bytes = build_4k_pap();

    let mut group = c.benchmark_group("pap.parse");
    group.bench_function("v1_4k_payload", |b| {
        b.iter(|| {
            let pap = Pap::parse(black_box(&pap_bytes)).expect("parse");
            black_box(pap);
        });
    });
    group.finish();
}

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