/// DreamStack Compiler & Layout Benchmarks
///
/// Measures:
/// - Compiler pipeline throughput (parse → analyze → emit)
/// - Signal graph construction at various scales
/// - Cassowary constraint solver with varying constraint counts

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

// ── Compiler pipeline benchmarks ────────────────────────────

fn counter_source() -> &'static str {
    r#"
let count = 0
on click { count = count + 1 }
view main = column [
  text { "Count: " + count }
  button("Increment") { on: click }
]
"#
}

fn todo_source() -> &'static str {
    r#"
let todos = []
let input = ""
let filter = "all"

on addTodo {
  todos = todos + [{ text: input, done: false }]
  input = ""
}

on toggleTodo {
  todos = todos
}

view main = column [
  row [
    text { input }
    button("Add") { on: addTodo }
  ]
  column [
    for todo in todos {
      row [
        text { todo.text }
      ]
    }
  ]
  row [
    button("All") { on: toggleTodo }
    button("Active") { on: toggleTodo }
    button("Done") { on: toggleTodo }
  ]
]
"#
}

/// Generate a stress-test source with N signals and derived values.
fn signal_chain_source(n: usize) -> String {
    let mut src = String::new();
    src.push_str("let s0 = 0\n");
    for i in 1..n {
        src.push_str(&format!("let s{i} = s{} + 1\n", i - 1));
    }
    src.push_str(&format!(
        "view main = column [\n  text {{ \"Last: \" + s{} }}\n]\n",
        n - 1
    ));
    src
}

/// Generate a fan-out source: 1 root signal, N derived dependents.
fn fan_out_source(n: usize) -> String {
    let mut src = String::from("let root = 0\n");
    for i in 0..n {
        src.push_str(&format!("let d{i} = root + {i}\n"));
    }
    // View shows last derived
    src.push_str(&format!(
        "view main = column [\n  text {{ \"d0: \" + d0 }}\n]\n",
    ));
    src
}

fn compile_source(source: &str) -> String {
    let program = ds_parser::parse(source).expect("parse failed");
    let (graph, views) = ds_analyzer::analyze(&program);
    ds_codegen::JsEmitter::emit_html(&program, &graph, &views)
}

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

    group.bench_function("counter", |b| {
        let src = counter_source();
        b.iter(|| compile_source(black_box(src)));
    });

    group.bench_function("todo", |b| {
        let src = todo_source();
        b.iter(|| compile_source(black_box(src)));
    });

    group.finish();
}

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

    for &n in &[10, 50, 100, 500, 1000] {
        let src = signal_chain_source(n);
        group.bench_with_input(BenchmarkId::from_parameter(n), &src, |b, src| {
            b.iter(|| compile_source(black_box(src)));
        });
    }

    group.finish();
}

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

    for &n in &[10, 50, 100, 500, 1000] {
        let src = fan_out_source(n);
        group.bench_with_input(BenchmarkId::from_parameter(n), &src, |b, src| {
            b.iter(|| compile_source(black_box(src)));
        });
    }

    group.finish();
}

// ── Constraint solver benchmarks ────────────────────────────

fn bench_constraint_solver(c: &mut Criterion) {
    use ds_layout::{LayoutSolver, Variable, Constraint, Strength};

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

    for &n in &[10, 50, 100, 500] {
        group.bench_with_input(BenchmarkId::new("panel_chain", n), &n, |b, &n| {
            b.iter(|| {
                let mut solver = LayoutSolver::new();
                // Create a chain of N panels: each starts where the previous ends
                let mut vars = Vec::new();
                for _ in 0..n {
                    let x = Variable::new();
                    let w = Variable::new();
                    vars.push((x, w));
                }
                // First panel at x=0, width=100
                solver.add_constraint(Constraint::eq_const(vars[0].0, 0.0, Strength::Required));
                solver.add_constraint(Constraint::eq_const(vars[0].1, 100.0, Strength::Required));
                // Each subsequent panel: x_i = x_{i-1} + w_{i-1}
                for i in 1..n {
                    solver.add_constraint(Constraint::sum_eq(
                        vars[i - 1].0,
                        vars[i - 1].1,
                        0.0,
                        Strength::Required,
                    ));
                    solver.add_constraint(Constraint::eq_const(vars[i].1, 100.0, Strength::Required));
                }
                solver.solve();
                black_box(solver.get_value(vars[n - 1].0));
            });
        });
    }

    group.finish();
}

criterion_group!(
    benches,
    bench_compiler_pipeline,
    bench_signal_chain,
    bench_fan_out,
    bench_constraint_solver,
);
criterion_main!(benches);
