#![allow(clippy::unwrap_used)]

//! Criterion benchmarks for the ranking profile executor.
//!
//! Measures scoring latency for 200 candidates across different profiles:
//! - `trending`: exercises velocity reads + gate filtering
//! - `hot`: exercises view count reads + age computation
//! - `full_pipeline`: trending with gates (filter + sort + normalize)

use std::time::Duration;

use criterion::{Criterion, black_box, criterion_group, criterion_main};
use tidaldb::ranking::builtins::register_builtins;
use tidaldb::ranking::{ProfileExecutor, ProfileRegistry};
use tidaldb::schema::{DecaySpec, EntityId, EntityKind, SchemaBuilder, Timestamp, Window};
use tidaldb::signals::{NoopWalWriter, SignalLedger};

#[allow(clippy::cast_precision_loss)]
fn make_ledger_with_200_items() -> SignalLedger {
    let mut builder = SchemaBuilder::new();
    for sig in &["view", "share", "like"] {
        let _ = builder
            .signal(
                sig,
                EntityKind::Item,
                DecaySpec::Exponential {
                    half_life: Duration::from_secs(7 * 24 * 3600),
                },
            )
            .windows(&[Window::OneHour, Window::SevenDays])
            .velocity(true)
            .add();
    }
    let schema = builder.build().unwrap();
    let ledger = SignalLedger::new(schema, Box::new(NoopWalWriter));
    let base_ns = 1_708_000_000_000_000_000u64;
    for i in 0u64..200 {
        let entity_id = EntityId::new(i + 1);
        let ts = Timestamp::from_nanos(base_ns - i * 3_600_000_000_000);
        ledger
            .record_signal("view", entity_id, (200 - i) as f64, ts)
            .unwrap();
        ledger
            .record_signal("share", entity_id, (i % 10) as f64, ts)
            .unwrap();
        ledger
            .record_signal("like", entity_id, (i % 5) as f64, ts)
            .unwrap();
    }
    ledger
}

fn bench_score_200_trending(c: &mut Criterion) {
    let ledger = make_ledger_with_200_items();
    let mut registry = ProfileRegistry::new();
    register_builtins(&mut registry).unwrap();
    let profile = registry.get("trending").unwrap().clone();
    let executor = ProfileExecutor::new(&ledger);
    let candidates: Vec<EntityId> = (1..=200).map(EntityId::new).collect();
    let now = Timestamp::from_nanos(1_708_000_000_000_000_000u64);

    c.bench_function("score_200_trending", |b| {
        b.iter(|| executor.score(black_box(&candidates), black_box(&profile), black_box(now)));
    });
}

fn bench_score_200_hot(c: &mut Criterion) {
    let ledger = make_ledger_with_200_items();
    let mut registry = ProfileRegistry::new();
    register_builtins(&mut registry).unwrap();
    let profile = registry.get("hot").unwrap().clone();
    let executor = ProfileExecutor::new(&ledger);
    let candidates: Vec<EntityId> = (1..=200).map(EntityId::new).collect();
    let now = Timestamp::from_nanos(1_708_000_000_000_000_000u64);

    c.bench_function("score_200_hot", |b| {
        b.iter(|| executor.score(black_box(&candidates), black_box(&profile), black_box(now)));
    });
}

fn bench_score_200_full_pipeline(c: &mut Criterion) {
    let ledger = make_ledger_with_200_items();
    let mut registry = ProfileRegistry::new();
    register_builtins(&mut registry).unwrap();
    let profile = registry.get("trending").unwrap().clone();
    let executor = ProfileExecutor::new(&ledger);
    let candidates: Vec<EntityId> = (1..=200).map(EntityId::new).collect();
    let now = Timestamp::from_nanos(1_708_000_000_000_000_000u64);

    c.bench_function("score_200_full_pipeline", |b| {
        b.iter(|| executor.score(black_box(&candidates), black_box(&profile), black_box(now)));
    });
}

criterion_group!(
    benches,
    bench_score_200_trending,
    bench_score_200_hot,
    bench_score_200_full_pipeline
);
criterion_main!(benches);
