//! Benchmarks for Luci indexing and search operations.
//!
//! Run: `cargo bench -p luci-index`
//! Profile: `cargo bench -p luci-index -- --profile-time 10`

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

use luci_index::index::Index;
use luci_mapping::{FieldType,Mapping};
use serde_json::json;

use std::path::PathBuf;

fn bench_dir(name: &str) -> PathBuf {
    let dir = std::env::temp_dir().join(format!("luci_bench_{name}"));
    let _ = std::fs::remove_dir_all(&dir);
    dir
}

fn text_schema() -> Mapping {
    Mapping::builder()
        .field("title", FieldType::Text)
        .field("body", FieldType::Text)
        .field("tag", FieldType::Keyword)
        .field("price", FieldType::Float)
        .build()
}

fn build_index(name: &str, doc_count: usize) -> (PathBuf, Index) {
    let path = bench_dir(name);
    let mut index = Index::create_with_mapping(&path, text_schema()).unwrap();

    let tags = ["tech", "science", "sports", "politics", "entertainment"];
    for i in 0..doc_count {
        let tag = tags[i % tags.len()];
        index.add(json!({
            "title": format!("document number {i} about {tag}"),
            "body": format!("this is the body of document {i} discussing various {tag} topics and related subjects"),
            "tag": tag,
            "price": (i as f64 % 100.0) + 0.99,
        })).unwrap();
    }
    index.commit().unwrap();
    (path, index)
}

// --- Indexing benchmarks ---

fn bench_indexing(c: &mut Criterion) {
    let mut group = c.benchmark_group("indexing");

    for &count in &[100, 1_000, 10_000] {
        group.bench_with_input(
            BenchmarkId::new("add_docs", count),
            &count,
            |b, &count| {
                b.iter(|| {
                    let path = bench_dir(&format!("idx_{count}"));
                    let mut index = Index::create_with_mapping(&path, text_schema()).unwrap();
                    for i in 0..count {
                        index.add(json!({
                            "title": format!("doc {i}"),
                            "body": "some body text for benchmarking",
                            "tag": "bench",
                            "price": 9.99,
                        })).unwrap();
                    }
                    index.commit().unwrap();
                    let _ = std::fs::remove_dir_all(&path);
                });
            },
        );
    }

    group.finish();
}

// --- Search benchmarks ---

fn bench_term_search(c: &mut Criterion) {
    let (path, index) = build_index("term_search", 10_000);

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

    group.bench_function("term_query_keyword", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({"term": {"tag": "tech"}})),
                10,
            ).unwrap();
            black_box(results.total_hits);
        });
    });

    group.bench_function("match_query_single_term", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({"match": {"title": "document"}})),
                10,
            ).unwrap();
            black_box(results.total_hits);
        });
    });

    group.bench_function("match_query_multi_term", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({"match": {"body": "body text benchmarking"}})),
                10,
            ).unwrap();
            black_box(results.total_hits);
        });
    });

    group.bench_function("bool_must_filter", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({
                    "bool": {
                        "must": [{"match": {"title": "document"}}],
                        "filter": [{"term": {"tag": "tech"}}]
                    }
                })),
                10,
            ).unwrap();
            black_box(results.total_hits);
        });
    });

    group.bench_function("phrase_query", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({"match_phrase": {"body": "body text"}})),
                10,
            ).unwrap();
            black_box(results.total_hits);
        });
    });

    group.bench_function("match_all", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({"match_all": {}})),
                10,
            ).unwrap();
            black_box(results.total_hits);
        });
    });

    group.finish();
    let _ = std::fs::remove_dir_all(&path);
}

// --- Aggregation benchmarks ---

fn bench_aggregations(c: &mut Criterion) {
    let (path, index) = build_index("agg_bench", 10_000);

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

    group.bench_function("terms_agg", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({
                    "query": {"match_all": {}},
                    "aggs": {"by_tag": {"terms": {"field": "tag"}}},
                    "size": 0,
                })),
                0,
            ).unwrap();
            black_box(&results.aggregations);
        });
    });

    group.bench_function("avg_agg", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({
                    "query": {"match_all": {}},
                    "aggs": {"avg_price": {"avg": {"field": "price"}}},
                    "size": 0,
                })),
                0,
            ).unwrap();
            black_box(&results.aggregations);
        });
    });

    group.bench_function("terms_with_sub_avg", |b| {
        b.iter(|| {
            let results = index.search(
                black_box(json!({
                    "query": {"match_all": {}},
                    "aggs": {
                        "by_tag": {
                            "terms": {"field": "tag"},
                            "aggs": {"avg_price": {"avg": {"field": "price"}}}
                        }
                    },
                    "size": 0,
                })),
                0,
            ).unwrap();
            black_box(&results.aggregations);
        });
    });

    group.finish();
    let _ = std::fs::remove_dir_all(&path);
}

criterion_group!(benches, bench_indexing, bench_term_search, bench_aggregations);
criterion_main!(benches);
