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

const SIMPLE_T0: &str = "x: 1\ny: \"hello\"\na: [1, 2, 3]\n";

const SIMPLE_T1_NO_DECORATORS: &str = "\
+++
_dms_tier: 1
_dms_imports:
  + dialect: \"x\"
    version: \"1.0.0\"
    bind:
      \"|\": [\"tag\"]
+++
x: 1
y: \"hello\"
a: [1, 2, 3]
";

const T1_WITH_LEADING_DECORATORS: &str = "\
+++
_dms_tier: 1
_dms_imports:
  + dialect: \"x\"
    version: \"1.0.0\"
    bind:
      \"|\": [\"tag\"]
+++
|tag()
x: 1
|tag(class: \"x\", id: \"y\")
y: \"hello\"
|tag()
a: [1, 2, 3]
";

const T1_WITH_INNER_TRAILING: &str = "\
+++
_dms_tier: 1
_dms_imports:
  + dialect: \"x\"
    version: \"1.0.0\"
    bind:
      \"|\": [\"tag\"]
+++
x: |tag() 1 |tag()
y: |tag() \"hello\" |tag()
a: [|tag() 1, 2 |tag(), 3]
";

const T1_LARGE_DECORATIONS: &str = include_str!("./fixtures/large_t1.dms");

fn build_html_registry() -> tier1::InMemoryRegistry {
    let dialect = tier1::DialectSpec {
        name: "x".into(),
        version: "1.0.0".into(),
        version_strategy: tier1::VersionStrategy::default(),
        families: vec![tier1::FamilySpec {
            name: "tag".into(),
            default_sigils: vec!["|".into()],
            empty_default: dms::Value::Table(Default::default()),
            content_slot: Some("children".into()),
            params: tier1::ParamSignature::default(),
        }],
        structs: vec![],
    };
    tier1::InMemoryRegistry::new().with(dialect)
}

fn bench_decode(c: &mut Criterion) {
    let mut g = c.benchmark_group("decode");

    g.bench_function("tier0/simple", |b| {
        b.iter(|| decode_document(black_box(SIMPLE_T0)).unwrap())
    });

    g.bench_function("tier1/no_decorators", |b| {
        b.iter(|| tier1::decode_t1(black_box(SIMPLE_T1_NO_DECORATORS)).unwrap())
    });

    g.bench_function("tier1/leading_decorators", |b| {
        b.iter(|| tier1::decode_t1(black_box(T1_WITH_LEADING_DECORATORS)).unwrap())
    });

    g.bench_function("tier1/inner_trailing_flow", |b| {
        b.iter(|| tier1::decode_t1(black_box(T1_WITH_INNER_TRAILING)).unwrap())
    });

    let registry = build_html_registry();
    g.bench_function("tier1/with_registry", |b| {
        b.iter(|| {
            tier1::decode_t1_with_registry(black_box(T1_WITH_LEADING_DECORATORS), &registry)
                .unwrap()
        })
    });

    g.finish();
}

fn bench_decode_large(c: &mut Criterion) {
    let mut g = c.benchmark_group("decode_large");
    let registry = build_html_registry();

    g.bench_function("tier1/large_with_registry", |b| {
        b.iter(|| {
            tier1::decode_t1_with_registry(black_box(T1_LARGE_DECORATIONS), &registry).unwrap()
        })
    });

    g.finish();
}

// Parameterised bench: compare tier0 vs tier1/no-decorators vs tier1/decorators
// at a glance via BenchmarkId.
fn bench_decode_variants(c: &mut Criterion) {
    let inputs: &[(&str, &str)] = &[
        ("tier0", SIMPLE_T0),
        ("tier1_bare", SIMPLE_T1_NO_DECORATORS),
        ("tier1_leading", T1_WITH_LEADING_DECORATORS),
        ("tier1_flow", T1_WITH_INNER_TRAILING),
    ];

    let mut g = c.benchmark_group("decode_variants");
    for (label, src) in inputs {
        g.bench_with_input(BenchmarkId::from_parameter(label), src, |b, s| {
            b.iter(|| {
                // tier0 input goes through decode_document; tier1 through decode_t1
                if src.starts_with("+++") {
                    tier1::decode_t1(black_box(s)).unwrap();
                } else {
                    decode_document(black_box(s)).unwrap();
                }
            })
        });
    }
    g.finish();
}

criterion_group!(benches, bench_decode, bench_decode_large, bench_decode_variants);
criterion_main!(benches);
