#![allow(clippy::unwrap_used)]

//! Criterion benchmarks for the composable filter evaluation engine.
//!
//! All benchmarks use 10 000 entities with ~50% selectivity per leaf predicate.

use criterion::{Criterion, black_box, criterion_group, criterion_main};
use roaring::RoaringBitmap;
use tidaldb::storage::indexes::{BitmapIndex, FilterEvaluator, FilterExpr, RangeIndex};

const N: u32 = 10_000;

/// Build a populated set of indexes with `N` entities.
///
/// Layout:
/// - category: even → "jazz", odd → "blues"  (50% each)
/// - format:   id % 3 == 0 → "video", else → "audio"  (~33% / ~67%)
/// - duration: id seconds (1 … N)
fn make_indexes() -> (
    BitmapIndex,
    BitmapIndex,
    BitmapIndex,
    BitmapIndex,
    RangeIndex<u32>,
    RangeIndex<u64>,
    RoaringBitmap,
) {
    let cat = BitmapIndex::new("category");
    let fmt = BitmapIndex::new("format");
    let creator = BitmapIndex::new("creator");
    let tags = BitmapIndex::new("tags");
    let dur: RangeIndex<u32> = RangeIndex::new("duration");
    let ts: RangeIndex<u64> = RangeIndex::new("created_at");

    let base_ns: u64 = 1_708_000_000_000_000_000;

    let mut universe = RoaringBitmap::new();
    for id in 0..N {
        universe.insert(id);
        if id % 2 == 0 {
            cat.insert(id, "jazz");
        } else {
            cat.insert(id, "blues");
        }
        if id % 3 == 0 {
            fmt.insert(id, "video");
        } else {
            fmt.insert(id, "audio");
        }
        creator.insert(id, (id % 100).to_string());
        dur.insert(id, id + 1);
        ts.insert(id, base_ns - u64::from(id) * 86_400_000_000);
    }

    (cat, fmt, creator, tags, dur, ts, universe)
}

/// AND of two high-selectivity bitmap predicates.
///
/// Category "jazz" (50%) AND format "video" (33%) → ~17% selectivity.
/// Tests the short-circuit AND path and selectivity-ordered evaluation.
fn bench_bitmap_and_2(c: &mut Criterion) {
    let (cat, fmt, creator, tags, dur, ts, universe) = make_indexes();
    let ev = FilterEvaluator::new(&cat, &fmt, &creator, &tags, &dur, &ts, &universe);

    let expr = FilterExpr::And(vec![
        FilterExpr::CategoryEq("jazz".into()),
        FilterExpr::FormatEq("video".into()),
    ]);

    c.bench_function("filter_bitmap_and_2", |b| {
        b.iter(|| ev.evaluate(black_box(&expr)).into_bitmap());
    });
}

/// OR of two bitmap predicates covering the full universe.
///
/// Category "jazz" (50%) OR category "blues" (50%) → 100% selectivity.
/// Tests the union path, which cannot short-circuit.
fn bench_bitmap_or_2(c: &mut Criterion) {
    let (cat, fmt, creator, tags, dur, ts, universe) = make_indexes();
    let ev = FilterEvaluator::new(&cat, &fmt, &creator, &tags, &dur, &ts, &universe);

    let expr = FilterExpr::Or(vec![
        FilterExpr::CategoryEq("jazz".into()),
        FilterExpr::CategoryEq("blues".into()),
    ]);

    c.bench_function("filter_bitmap_or_2", |b| {
        b.iter(|| ev.evaluate(black_box(&expr)).into_bitmap());
    });
}

/// NOT of a 50%-selectivity predicate.
///
/// Tests the complement (universe XOR result) path.
fn bench_bitmap_not(c: &mut Criterion) {
    let (cat, fmt, creator, tags, dur, ts, universe) = make_indexes();
    let ev = FilterEvaluator::new(&cat, &fmt, &creator, &tags, &dur, &ts, &universe);

    let expr = FilterExpr::Not(Box::new(FilterExpr::CategoryEq("jazz".into())));

    c.bench_function("filter_bitmap_not", |b| {
        b.iter(|| ev.evaluate(black_box(&expr)).into_bitmap());
    });
}

/// Predicate check: convert bitmap result to predicate, call for 1 entity.
///
/// Models the per-candidate evaluation path in the query executor.
fn bench_predicate_check_single(c: &mut Criterion) {
    let (cat, fmt, creator, tags, dur, ts, universe) = make_indexes();
    let ev = FilterEvaluator::new(&cat, &fmt, &creator, &tags, &dur, &ts, &universe);

    let expr = FilterExpr::CategoryEq("jazz".into());
    let result = ev.evaluate(&expr);
    let predicate = result.into_predicate();

    c.bench_function("filter_predicate_check_single", |b| {
        b.iter(|| black_box(predicate(black_box(42u64))));
    });
}

criterion_group!(
    benches,
    bench_bitmap_and_2,
    bench_bitmap_or_2,
    bench_bitmap_not,
    bench_predicate_check_single,
);
criterion_main!(benches);
