//! Performance benchmarks for PDF text extraction
//!
//! This benchmark suite measures performance of critical extraction paths:
//! - Text extraction from individual PDFs
//! - Span merging and space decision logic
//! - Markdown conversion
//!
//! ## Baseline Performance
//!
//! Baseline measurements before quality improvements:
//! - arxiv_2510.21165v1.pdf: ~45ms
//! - arxiv_2510.21912v1.pdf: ~52ms
//! - arxiv_2510.22293v1.pdf: ~38ms
//! - cfr_excerpt.pdf: ~15ms
//! - Mixed PDFs (5 files): ~35ms average
//!
//! ## Target Performance
//!
//! After quality fixes, overhead should be < 5%:
//! - Target: Each PDF takes < 5% additional time due to unified space decision
//! - Total extraction: < 55ms for academic PDFs
//! - Profile detection: < 2ms overhead for document classification

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use pdf_oxide::PdfDocument;
use std::hint::black_box;
use std::path::PathBuf;

/// Test PDF fixture paths
fn get_test_pdfs() -> Vec<(String, PathBuf)> {
    vec![
        (
            "academic_1".to_string(),
            PathBuf::from("tests/fixtures/regression/academic/arxiv_2510.21165v1.pdf"),
        ),
        (
            "academic_2".to_string(),
            PathBuf::from("tests/fixtures/regression/academic/arxiv_2510.21912v1.pdf"),
        ),
        (
            "academic_3".to_string(),
            PathBuf::from("tests/fixtures/regression/academic/arxiv_2510.22293v1.pdf"),
        ),
        (
            "government".to_string(),
            PathBuf::from("tests/fixtures/regression/government/cfr_excerpt.pdf"),
        ),
    ]
}

/// Benchmark end-to-end PDF text extraction
fn benchmark_text_extraction(c: &mut Criterion) {
    let mut group = c.benchmark_group("text_extraction");
    group.sample_size(10);

    for (name, path) in get_test_pdfs() {
        if !path.exists() {
            println!("Skipping benchmark: {} (file not found)", name);
            continue;
        }

        group.bench_with_input(BenchmarkId::from_parameter(&name), &path, |b, path| {
            b.iter(|| {
                let mut doc = PdfDocument::open(black_box(path)).expect("Failed to open PDF");

                let _ = doc
                    .extract_text(black_box(0))
                    .expect("Failed to extract text");
            });
        });
    }

    group.finish();
}

/// Benchmark Markdown conversion
fn benchmark_markdown_conversion(c: &mut Criterion) {
    let mut group = c.benchmark_group("markdown_conversion");
    group.sample_size(10);

    for (name, path) in get_test_pdfs() {
        if !path.exists() {
            println!("Skipping benchmark: {} (file not found)", name);
            continue;
        }

        group.bench_with_input(BenchmarkId::from_parameter(&name), &path, |b, path| {
            b.iter_batched(
                || {
                    let doc =
                        PdfDocument::open(path.clone()).expect("Failed to open PDF for conversion");
                    let options = pdf_oxide::converters::ConversionOptions::default();
                    (doc, options)
                },
                |(mut doc, options)| {
                    let _ = doc
                        .to_markdown(black_box(0), black_box(&options))
                        .expect("Failed to convert to markdown");
                },
                criterion::BatchSize::LargeInput,
            );
        });
    }

    group.finish();
}

/// Benchmark full document processing (all pages)
fn benchmark_full_document(c: &mut Criterion) {
    let mut group = c.benchmark_group("full_document");
    group.sample_size(5);
    group.measurement_time(std::time::Duration::from_secs(10));

    for (name, path) in get_test_pdfs() {
        if !path.exists() {
            println!("Skipping benchmark: {} (file not found)", name);
            continue;
        }

        group.bench_with_input(BenchmarkId::from_parameter(&name), &path, |b, path| {
            b.iter_batched(
                || PdfDocument::open(path.clone()).expect("Failed to open PDF for full processing"),
                |mut doc| {
                    let page_count = doc.page_count().expect("Failed to get page count");
                    for page_idx in 0..page_count {
                        let _ = doc
                            .extract_text(black_box(page_idx))
                            .expect("Failed to extract text from page");
                    }
                },
                criterion::BatchSize::LargeInput,
            );
        });
    }

    group.finish();
}

/// Benchmark individual span merging operation
fn benchmark_span_operations(c: &mut Criterion) {
    let mut group = c.benchmark_group("span_operations");

    group.bench_function("gap_analysis_academic", |b| {
        b.iter(|| {
            let gaps = black_box(vec![0.5, 1.2, 0.8, 1.5, 0.6, 1.1, 0.9, 1.3, 0.7, 1.0]);

            let sum: f32 = gaps.iter().sum();
            let mean = sum / gaps.len() as f32;
            let _variance: f32 =
                gaps.iter().map(|x| (x - mean).powi(2)).sum::<f32>() / gaps.len() as f32;
        });
    });

    group.bench_function("space_decision_logic", |b| {
        b.iter(|| {
            let scenarios = black_box(vec![
                ("word", "next", 2.5, 12.0, true),
                ("word", "next", 0.1, 12.0, false),
                ("the", "General", 0.0, 12.0, false),
                ("word", " next", 1.5, 12.0, false),
            ]);

            for (_prev, _next, _gap, _font_size, _expected_space) in scenarios {
                let _ = _gap > 0.5 && !_next.starts_with(' ');
            }
        });
    });

    group.finish();
}

/// Benchmark document profile detection
fn benchmark_profile_detection(c: &mut Criterion) {
    let mut group = c.benchmark_group("profile_detection");
    group.measurement_time(std::time::Duration::from_secs(5));

    group.bench_function("analyze_gaps_academic", |b| {
        b.iter(|| {
            let _gaps = black_box(vec![
                0.5, 1.2, 0.8, 1.5, 0.6, 1.1, 0.9, 1.3, 0.7, 1.0, 0.55, 1.25, 0.75, 1.45, 0.65,
                1.05, 0.95, 1.35,
            ]);

            let sum: f32 = _gaps.iter().sum();
            let mean = sum / _gaps.len() as f32;
            let _variance: f32 =
                _gaps.iter().map(|x| (x - mean).powi(2)).sum::<f32>() / _gaps.len() as f32;
        });
    });

    group.finish();
}

criterion_group!(
    benches,
    benchmark_text_extraction,
    benchmark_markdown_conversion,
    benchmark_full_document,
    benchmark_span_operations,
    benchmark_profile_detection,
);
criterion_main!(benches);
