//! Benchmarks for temporal operations including extended date support.
//!
//! Run with: cargo bench --bench temporal_bench
//!
//! Results are stored in target/criterion/

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::hint::black_box;
use std::collections::HashMap;

use ocg::result::extended_temporal::{ExtendedDate, NxDate};
use ocg::{execute_with_params, PropertyGraph};

// ============================================================================
// ExtendedDate Core Operations
// ============================================================================

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

    // Standard year (chrono path)
    group.bench_function("standard_year_2024", |b| {
        b.iter(|| NxDate::from_ymd(black_box(2024), black_box(6), black_box(15)))
    });

    // Extended year (custom path)
    group.bench_function("extended_year_500M", |b| {
        b.iter(|| NxDate::from_ymd(black_box(500_000_000), black_box(6), black_box(15)))
    });

    // Extreme year
    group.bench_function("extreme_year_999M", |b| {
        b.iter(|| NxDate::from_ymd(black_box(999_999_999), black_box(12), black_box(31)))
    });

    // Negative extended year
    group.bench_function("negative_year_-500M", |b| {
        b.iter(|| NxDate::from_ymd(black_box(-500_000_000), black_box(1), black_box(1)))
    });

    group.finish();
}

fn bench_epoch_days_calculation(c: &mut Criterion) {
    let mut group = c.benchmark_group("epoch_days");
    group.throughput(Throughput::Elements(1));

    // Standard date
    let standard_date = ExtendedDate::from_ymd(2024, 6, 15).unwrap();
    group.bench_function("standard_year", |b| {
        b.iter(|| black_box(&standard_date).to_epoch_days())
    });

    // Extended date
    let extended_date = ExtendedDate::from_ymd(500_000_000, 6, 15).unwrap();
    group.bench_function("extended_year_500M", |b| {
        b.iter(|| black_box(&extended_date).to_epoch_days())
    });

    // Extreme date
    let extreme_date = ExtendedDate::from_ymd(999_999_999, 12, 31).unwrap();
    group.bench_function("extreme_year_999M", |b| {
        b.iter(|| black_box(&extreme_date).to_epoch_days())
    });

    // Negative date
    let negative_date = ExtendedDate::from_ymd(-999_999_999, 1, 1).unwrap();
    group.bench_function("negative_year_-999M", |b| {
        b.iter(|| black_box(&negative_date).to_epoch_days())
    });

    group.finish();
}

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

    // Add days - standard
    let standard = NxDate::from_ymd(2024, 6, 15).unwrap();
    group.bench_function("add_days_standard", |b| {
        b.iter(|| {
            match &standard {
                NxDate::Standard(d) => {
                    let _ = black_box(*d + chrono::Duration::days(100));
                }
                _ => {}
            }
        })
    });

    // Add days - extended
    let extended = ExtendedDate::from_ymd(500_000_000, 6, 15).unwrap();
    group.bench_function("add_days_extended", |b| {
        b.iter(|| black_box(&extended).add_days(black_box(100)))
    });

    // Add months - extended
    group.bench_function("add_months_extended", |b| {
        b.iter(|| black_box(&extended).add_months(black_box(12)))
    });

    // Add many days
    group.bench_function("add_days_10000", |b| {
        b.iter(|| black_box(&extended).add_days(black_box(10_000)))
    });

    group.finish();
}

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

    // NxDate comparison (standard vs standard)
    let std1 = NxDate::from_ymd(2024, 6, 15).unwrap();
    let std2 = NxDate::from_ymd(2024, 12, 31).unwrap();
    group.bench_function("standard_vs_standard", |b| {
        b.iter(|| black_box(&std1) < black_box(&std2))
    });

    // NxDate comparison (extended vs extended)
    let ext1 = NxDate::from_ymd(500_000_000, 6, 15).unwrap();
    let ext2 = NxDate::from_ymd(500_000_001, 1, 1).unwrap();
    group.bench_function("extended_vs_extended", |b| {
        b.iter(|| black_box(&ext1) < black_box(&ext2))
    });

    // Mixed comparison (standard vs extended)
    group.bench_function("standard_vs_extended", |b| {
        b.iter(|| black_box(&std1) < black_box(&ext1))
    });

    group.finish();
}

// ============================================================================
// Query-Level Benchmarks
// ============================================================================

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

    // Standard date via query
    group.bench_function("query_standard_date", |b| {
        let mut graph = PropertyGraph::new();
        let params = HashMap::new();
        b.iter(|| {
            execute_with_params(
                &mut graph,
                black_box("RETURN date({year: 2024, month: 6, day: 15})"),
                params.clone(),
            )
        })
    });

    // Extended date via query
    group.bench_function("query_extended_date", |b| {
        let mut graph = PropertyGraph::new();
        let params = HashMap::new();
        b.iter(|| {
            execute_with_params(
                &mut graph,
                black_box("RETURN date({year: 500000000, month: 6, day: 15})"),
                params.clone(),
            )
        })
    });

    group.finish();
}

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

    // Standard date + duration
    group.bench_function("query_standard_add_duration", |b| {
        let mut graph = PropertyGraph::new();
        let params = HashMap::new();
        b.iter(|| {
            execute_with_params(
                &mut graph,
                black_box("RETURN date({year: 2024, month: 6, day: 15}) + duration({days: 100})"),
                params.clone(),
            )
        })
    });

    // Extended date + duration
    group.bench_function("query_extended_add_duration", |b| {
        let mut graph = PropertyGraph::new();
        let params = HashMap::new();
        b.iter(|| {
            execute_with_params(
                &mut graph,
                black_box("RETURN date({year: 500000000, month: 6, day: 15}) + duration({days: 100})"),
                params.clone(),
            )
        })
    });

    // Date difference
    group.bench_function("query_date_difference", |b| {
        let mut graph = PropertyGraph::new();
        let params = HashMap::new();
        b.iter(|| {
            execute_with_params(
                &mut graph,
                black_box("RETURN date({year: 2024, month: 12, day: 31}) - date({year: 2024, month: 1, day: 1})"),
                params.clone(),
            )
        })
    });

    // Extended date difference
    group.bench_function("query_extended_date_difference", |b| {
        let mut graph = PropertyGraph::new();
        let params = HashMap::new();
        b.iter(|| {
            execute_with_params(
                &mut graph,
                black_box("RETURN date({year: 500000000, month: 12, day: 31}) - date({year: 500000000, month: 1, day: 1})"),
                params.clone(),
            )
        })
    });

    group.finish();
}

// ============================================================================
// Throughput Benchmarks (simulating index operations)
// ============================================================================

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

    // Create test data
    let dates: Vec<ExtendedDate> = (0..1000)
        .map(|i| ExtendedDate::from_ymd(2024 + i as i64, (i % 12 + 1) as u32, (i % 28 + 1) as u32).unwrap())
        .collect();

    group.throughput(Throughput::Elements(1000));
    group.bench_function("1000_standard_dates", |b| {
        b.iter(|| {
            for date in &dates {
                black_box(date.to_epoch_days());
            }
        })
    });

    // Extended dates
    let extended_dates: Vec<ExtendedDate> = (0..1000)
        .map(|i| ExtendedDate::from_ymd(500_000_000 + i as i64, (i % 12 + 1) as u32, (i % 28 + 1) as u32).unwrap())
        .collect();

    group.bench_function("1000_extended_dates", |b| {
        b.iter(|| {
            for date in &extended_dates {
                black_box(date.to_epoch_days());
            }
        })
    });

    group.finish();
}

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

    // Standard range
    group.bench_function("standard_range", |b| {
        b.iter(|| ExtendedDate::from_epoch_days(black_box(19889))) // 2024-06-15
    });

    // Extended range (year 500M)
    let epoch_days_500m = ExtendedDate::from_ymd(500_000_000, 6, 15).unwrap().to_epoch_days();
    group.bench_function("extended_range", |b| {
        b.iter(|| ExtendedDate::from_epoch_days(black_box(epoch_days_500m)))
    });

    group.finish();
}

// ============================================================================
// Scaling Benchmarks
// ============================================================================

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

    let years = [
        1i64,
        1_000,
        1_000_000,
        100_000_000,
        500_000_000,
        999_999_999,
    ];

    for year in years {
        let date = ExtendedDate::from_ymd(year, 6, 15).unwrap();
        group.bench_with_input(BenchmarkId::new("epoch_days", year), &date, |b, d| {
            b.iter(|| black_box(d).to_epoch_days())
        });
    }

    group.finish();
}

// ============================================================================
// Criterion Configuration
// ============================================================================

criterion_group!(
    benches,
    bench_extended_date_creation,
    bench_epoch_days_calculation,
    bench_date_arithmetic,
    bench_date_comparison,
    bench_date_query_creation,
    bench_date_query_arithmetic,
    bench_epoch_days_throughput,
    bench_from_epoch_days,
    bench_year_scaling,
);

criterion_main!(benches);
