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

use calcpad_engine::context::EvalContext;
use calcpad_engine::pipeline::{eval_line, eval_sheet};
use calcpad_engine::SheetContext;

// --- Single-line benchmarks ---

fn bench_single_line_arithmetic(c: &mut Criterion) {
    c.bench_function("single_line_arithmetic", |b| {
        b.iter(|| {
            let mut ctx = EvalContext::new();
            eval_line(black_box("(3 + 4) * 2 ^ 3 - 1"), &mut ctx)
        })
    });
}

fn bench_single_line_unit_conversion(c: &mut Criterion) {
    c.bench_function("single_line_unit_conversion", |b| {
        b.iter(|| {
            let mut ctx = EvalContext::new();
            eval_line(black_box("5kg in lb"), &mut ctx)
        })
    });
}

// --- Sheet benchmarks (pipeline API) ---

fn bench_100_line_sheet(c: &mut Criterion) {
    let lines: Vec<String> = (0..100)
        .map(|i| {
            if i == 0 {
                "x = 1".to_string()
            } else {
                format!("x = x + {}", i)
            }
        })
        .collect();
    let line_refs: Vec<&str> = lines.iter().map(|s| s.as_str()).collect();

    c.bench_function("100_line_sheet_pipeline", |b| {
        b.iter(|| {
            let mut ctx = EvalContext::new();
            eval_sheet(black_box(&line_refs), &mut ctx)
        })
    });
}

fn bench_variable_heavy_sheet(c: &mut Criterion) {
    let mut lines: Vec<String> = Vec::with_capacity(50);
    for i in 0..10 {
        lines.push(format!("v{} = {}", i, i * 10 + 1));
    }
    for i in 10..50 {
        let a = i % 10;
        let b = (i + 3) % 10;
        lines.push(format!("r{} = v{} + v{}", i, a, b));
    }
    let line_refs: Vec<&str> = lines.iter().map(|s| s.as_str()).collect();

    c.bench_function("variable_heavy_sheet_dag", |b| {
        b.iter(|| {
            let mut ctx = EvalContext::new();
            eval_sheet(black_box(&line_refs), &mut ctx)
        })
    });
}

// --- SheetContext benchmarks (with dependency tracking) ---

fn bench_sheet_context_500_lines(c: &mut Criterion) {
    let lines: Vec<String> = (0..500)
        .map(|i| match i % 5 {
            0 => format!("x{} = {}", i, i),
            1 => format!("{} + {} * {}", i, i + 1, i + 2),
            2 => format!("{}kg in g", (i as f64) * 0.1),
            3 => format!("// Comment line {}", i),
            4 => format!("sqrt({})", (i * i) as f64),
            _ => unreachable!(),
        })
        .collect();

    c.bench_function("sheet_context_500_lines_full_eval", |b| {
        b.iter(|| {
            let mut sheet = SheetContext::new();
            for (i, line) in lines.iter().enumerate() {
                sheet.set_line(i, line);
            }
            sheet.eval()
        })
    });
}

fn bench_sheet_context_incremental_edit(c: &mut Criterion) {
    let lines: Vec<String> = (0..500)
        .map(|i| match i % 3 {
            0 => format!("x{} = {}", i, i),
            1 => format!("{} + {}", i, i + 1),
            2 => format!("sqrt({})", (i * i) as f64),
            _ => unreachable!(),
        })
        .collect();

    let mut sheet = SheetContext::new();
    for (i, line) in lines.iter().enumerate() {
        sheet.set_line(i, line);
    }
    sheet.eval();

    c.bench_function("sheet_context_incremental_single_edit", |b| {
        let mut s = sheet.clone();
        let mut counter = 0;
        b.iter(|| {
            counter += 1;
            // Edit a line that doesn't affect others (no variable)
            s.set_line(250, &format!("{} + {}", counter, counter + 1));
            s.eval()
        })
    });
}

fn bench_sheet_context_incremental_variable_change(c: &mut Criterion) {
    let mut sheet = SheetContext::new();
    sheet.set_line(0, "base = 100");
    for i in 1..500 {
        if i % 10 == 0 {
            sheet.set_line(i, "base + 1");
        } else {
            sheet.set_line(i, &format!("{} + {}", i, i + 1));
        }
    }
    sheet.eval();

    c.bench_function("sheet_context_incremental_variable_change", |b| {
        let mut s = sheet.clone();
        let mut counter = 100;
        b.iter(|| {
            counter += 1;
            s.set_line(0, &format!("base = {}", counter));
            s.eval()
        })
    });
}

criterion_group!(
    benches,
    bench_single_line_arithmetic,
    bench_single_line_unit_conversion,
    bench_100_line_sheet,
    bench_variable_heavy_sheet,
    bench_sheet_context_500_lines,
    bench_sheet_context_incremental_edit,
    bench_sheet_context_incremental_variable_change,
);
criterion_main!(benches);
