//! Infrastructure overhead profiling: storage reopen, JSON parsing, top-K collection.

use super::harness::*;
use luci_index::index::Index;
use luci_storage::SingleFileDirectory;
use luci_index::reader::IndexReader;
use serde_json::json;

use std::time::Instant;

const ITERS: usize = 50;

/// Measure the per-query overhead of Index::search() reopening SingleFileDirectory.
#[test]
fn index_search_reopen_cost() {
    println!("\n=== Overhead: Index.search() Reopen Cost ===\n");

    let (path, mut index) = build_text_corpus("reopen_cost", 10_000);

    // Measure raw search via the Index facade (includes SingleFileDirectory::open)
    let mut times = Vec::new();
    for _ in 0..ITERS {
        let start = Instant::now();
        let _ = index.search(json!({"term": {"tag": "tech"}}), 10).unwrap();
        times.push(start.elapsed());
    }
    times.sort();
    let facade_p50 = times[times.len() / 2];

    // Measure search with pre-opened reader (no reopen)
    let storage = SingleFileDirectory::open(&path).unwrap();
    let reader = IndexReader::open(&storage).unwrap();
    let searcher = luci_index::search::searcher::Searcher::with_analyzers(
        reader.into_segments(),
        luci_analysis::AnalyzerRegistry::new(),
    );

    let query = luci_index::query::term::TermQuery {
        field: "tag".into(),
        value: "tech".into(),
    };
    let mut times2 = Vec::new();
    for _ in 0..ITERS {
        let start = Instant::now();
        let _ = searcher.search_query(&query, 10, 0, &luci_index::search::SourceFilter::Enabled, &None, false, &[]);
        times2.push(start.elapsed());
    }
    times2.sort();
    let direct_p50 = times2[times2.len() / 2];

    let overhead = facade_p50.saturating_sub(direct_p50);

    println!("Index.search() (with reopen):  p50={:.1}us", facade_p50.as_micros() as f64);
    println!("Searcher.search_query() (pre-opened): p50={:.1}us", direct_p50.as_micros() as f64);
    println!("Reopen overhead:               {:.1}us ({:.0}%)",
        overhead.as_micros() as f64,
        overhead.as_micros() as f64 / facade_p50.as_micros().max(1) as f64 * 100.0);

    cleanup(&path);
}

/// Measure query JSON parsing overhead (isolated from search).
#[test]
fn json_parsing_overhead() {
    println!("\n=== Overhead: JSON Query Parsing ===\n");

    let queries = vec![
        ("simple_term", json!({"term": {"tag": "tech"}})),
        ("bool_2clause", json!({"bool": {"must": [{"match": {"title": "doc"}}, {"term": {"tag": "tech"}}]}})),
        ("complex_bool", json!({"bool": {
            "must": [{"match": {"title": "document"}}],
            "should": [{"match": {"body": "content"}}],
            "must_not": [{"term": {"tag": "sports"}}],
            "filter": [{"exists": {"field": "price"}}]
        }})),
        ("nested", json!({"nested": {"path": "offers", "query": {"bool": {"must": [
            {"term": {"offers.seller": "alice"}},
            {"term": {"offers.price": "100"}}
        ]}}}})),
    ];

    for (name, query) in &queries {
        let mut times = Vec::new();
        for _ in 0..1_000 {
            let start = Instant::now();
            let ast = luci_index::query::parser::parse_query(query).unwrap();
            let _q = luci_index::query::convert::ast_to_query(&ast);
            times.push(start.elapsed());
        }
        times.sort();
        let p50 = times[times.len() / 2];
        println!("{name:<20} p50={:.1}us", p50.as_micros() as f64);
    }
}
