mod common;

use common::Dataset;
use oo_ide::file_index::NucleoSearch;
use oo_ide::utils::fuzzy::filter_and_rank;

// ── Loading ───────────────────────────────────────────────────────────────────
//
// Measures the cost of building the full Vec<PathBuf> from scratch (string
// formatting + PathBuf allocation). This represents what the app pays before
// any filtering can begin.

#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn load(bencher: divan::Bencher, dataset: Dataset) {
    bencher.bench(|| dataset.generate());
}

// ── Filtering ─────────────────────────────────────────────────────────────────
//
// Path list is generated once per benchmark variant (outside the timed loop)
// via with_inputs; only filter_and_rank is measured.

/// Empty query: all items returned in original order — collection-only overhead.
#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn filter_empty(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| dataset.generate())
        .bench_refs(|paths| filter_and_rank(paths, "").len());
}

/// Broad match: "file" hits nearly every entry in all datasets.
#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn filter_broad(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| dataset.generate())
        .bench_refs(|paths| filter_and_rank(paths, "file").len());
}

/// Medium selectivity: exercises the filename-vs-full-path fallback scoring.
#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn filter_medium(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| dataset.generate())
        .bench_refs(|paths| filter_and_rank(paths, "component").len());
}

/// Multi-token: each token requires an independent subsequence scan per entry.
#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn filter_multi_token(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| dataset.generate())
        .bench_refs(|paths| filter_and_rank(paths, "service validator").len());
}

/// No-match: every entry is rejected — worst-case rejection path.
#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn filter_no_match(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| dataset.generate())
        .bench_refs(|paths| filter_and_rank(paths, "zzzzzzzzz").len());
}

// ── Typing simulation ─────────────────────────────────────────────────────────
//
// Runs filter_and_rank for each prefix of "component" in a single timed
// iteration, simulating what the app does as a user types in the file picker.

#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn incremental_typing(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| dataset.generate())
        .bench_refs(|paths| {
            let queries = ["c", "co", "com", "comp", "component"];
            for q in queries {
                filter_and_rank(paths, q);
            }
        });
}

// ── Nucleo filtering ──────────────────────────────────────────────────────────
//
// Same queries as above but using FileIndex + NucleoSearch.  These benches
// exist to measure the speed-up from nucleo over the hand-rolled fuzzy matcher.

#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn nucleo_filter_broad(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| (dataset.to_file_index(), NucleoSearch::new()))
        .bench_refs(|(index, nucleo)| nucleo.search(index, "file").len());
}

#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn nucleo_filter_medium(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| (dataset.to_file_index(), NucleoSearch::new()))
        .bench_refs(|(index, nucleo)| nucleo.search(index, "component").len());
}

#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn nucleo_filter_no_match(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| (dataset.to_file_index(), NucleoSearch::new()))
        .bench_refs(|(index, nucleo)| nucleo.search(index, "zzzzzzzzz").len());
}

#[divan::bench(args = [Dataset::Monorepo, Dataset::Pathological, Dataset::SearchStress])]
fn nucleo_incremental_typing(bencher: divan::Bencher, dataset: Dataset) {
    bencher
        .with_inputs(|| (dataset.to_file_index(), NucleoSearch::new()))
        .bench_refs(|(index, nucleo)| {
            let queries = ["c", "co", "com", "comp", "component"];
            for q in queries {
                nucleo.search(index, q);
            }
        });
}

fn main() {
    divan::main();
}