use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
use fff_query_parser::*;

fn bench_parse_simple(c: &mut Criterion) {
    let parser = QueryParser::default();

    c.bench_function("parse_simple_text", |b| {
        b.iter(|| parser.parse(black_box("hello world")));
    });

    c.bench_function("parse_extension", |b| {
        b.iter(|| parser.parse(black_box("*.rs")));
    });

    c.bench_function("parse_text_with_extension", |b| {
        b.iter(|| parser.parse(black_box("name *.rs")));
    });
}

fn bench_parse_complex(c: &mut Criterion) {
    let parser = QueryParser::default();

    c.bench_function("parse_complex_mixed", |b| {
        b.iter(|| parser.parse(black_box("src name *.rs !test /lib/ status:modified")));
    });

    c.bench_function("parse_glob", |b| {
        b.iter(|| parser.parse(black_box("**/*.rs")));
    });

    c.bench_function("parse_multiple_constraints", |b| {
        b.iter(|| parser.parse(black_box("*.rs *.toml *.md !test !node_modules /src/")));
    });
}

fn bench_parse_realistic_queries(c: &mut Criterion) {
    let parser = QueryParser::default();

    let queries = vec![
        "file",
        "test",
        "mod.rs",
        "src/*.rs",
        "lib test",
        "*.rs !test",
        "src/lib/*.rs",
        "/src/ name",
        "status:modified *.rs",
        "type:rust test !node_modules",
    ];

    let mut group = c.benchmark_group("realistic_queries");
    for query in queries.iter() {
        group.throughput(Throughput::Bytes(query.len() as u64));
        group.bench_with_input(BenchmarkId::from_parameter(query), query, |b, q| {
            b.iter(|| parser.parse(black_box(q)));
        });
    }
    group.finish();
}

fn bench_parse_various_lengths(c: &mut Criterion) {
    let parser = QueryParser::default();

    let short = "*.rs";
    let medium = "src name *.rs !test";
    let long = "src lib test name *.rs *.toml !node_modules !test /src/ /lib/ status:modified";
    let very_long =
        "a b c d e f g h i j k l m n o p q r s t u v w x y z *.rs *.toml *.md *.txt *.js";

    let mut group = c.benchmark_group("query_lengths");

    group.throughput(Throughput::Bytes(short.len() as u64));
    group.bench_with_input(BenchmarkId::new("short", short.len()), &short, |b, q| {
        b.iter(|| parser.parse(black_box(q)));
    });

    group.throughput(Throughput::Bytes(medium.len() as u64));
    group.bench_with_input(BenchmarkId::new("medium", medium.len()), &medium, |b, q| {
        b.iter(|| parser.parse(black_box(q)));
    });

    group.throughput(Throughput::Bytes(long.len() as u64));
    group.bench_with_input(BenchmarkId::new("long", long.len()), &long, |b, q| {
        b.iter(|| parser.parse(black_box(q)));
    });

    group.throughput(Throughput::Bytes(very_long.len() as u64));
    group.bench_with_input(
        BenchmarkId::new("very_long", very_long.len()),
        &very_long,
        |b, q| {
            b.iter(|| parser.parse(black_box(q)));
        },
    );

    group.finish();
}

fn bench_config_comparison(c: &mut Criterion) {
    let file_picker = QueryParser::new(FileSearchConfig);
    let grep = QueryParser::new(GrepConfig);

    let query = "src name *.rs !test";

    let mut group = c.benchmark_group("config_comparison");

    group.bench_function("file_picker_config", |b| {
        b.iter(|| file_picker.parse(black_box(query)));
    });

    group.bench_function("grep_config", |b| {
        b.iter(|| grep.parse(black_box(query)));
    });

    group.finish();
}

fn bench_constraint_types(c: &mut Criterion) {
    let parser = QueryParser::default();

    let mut group = c.benchmark_group("constraint_types");

    group.bench_function("extension", |b| {
        b.iter(|| parser.parse(black_box("*.rs")));
    });

    group.bench_function("glob", |b| {
        b.iter(|| parser.parse(black_box("**/*.rs")));
    });

    group.bench_function("exclude", |b| {
        b.iter(|| parser.parse(black_box("!test")));
    });

    group.bench_function("path_segment", |b| {
        b.iter(|| parser.parse(black_box("/src/")));
    });

    group.bench_function("git_status", |b| {
        b.iter(|| parser.parse(black_box("status:modified")));
    });

    group.bench_function("file_type", |b| {
        b.iter(|| parser.parse(black_box("type:rust")));
    });

    group.finish();
}

fn bench_worst_case(c: &mut Criterion) {
    let parser = QueryParser::default();

    // Worst case: many constraints that all need to be checked
    let worst_case = "a b c d e f g h i j k l m n o p q r s t u v w x y z";

    c.bench_function("worst_case_many_text_tokens", |b| {
        b.iter(|| parser.parse(black_box(worst_case)));
    });

    // Many constraints
    let many_constraints = "*.rs *.toml *.md *.txt *.js *.ts *.jsx *.tsx *.vue *.svelte";

    c.bench_function("worst_case_many_constraints", |b| {
        b.iter(|| parser.parse(black_box(many_constraints)));
    });
}

criterion_group!(
    benches,
    bench_parse_simple,
    bench_parse_complex,
    bench_parse_realistic_queries,
    bench_parse_various_lengths,
    bench_config_comparison,
    bench_constraint_types,
    bench_worst_case,
);

criterion_main!(benches);
