//! Benchmark suite for JVMRS performance characteristics
//!
//! This module provides comprehensive benchmarks comparing JVMRS
//! performance against baseline expectations and other JVM implementations.

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use jvmrs::Interpreter;
use std::time::Duration;

/// Benchmark class loading performance
fn benchmark_class_loading(c: &mut Criterion) {
    let mut group = c.benchmark_group("class_loading");

    group.bench_function("load_single_class", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            black_box(interpreter.load_class("java/lang/Object").ok())
        });
    });

    group.bench_function("load_multiple_classes", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            let classes = vec![
                "java/lang/Object",
                "java/lang/String",
                "java/util/ArrayList",
                "java/util/HashMap",
            ];
            for class in classes {
                black_box(interpreter.load_class(class).ok());
            }
        });
    });

    group.finish();
}

/// Benchmark method invocation performance
fn benchmark_method_invocation(c: &mut Criterion) {
    let mut group = c.benchmark_group("method_invocation");

    // Test different invocation patterns
    let invocation_types = vec![
        ("static_method", "()I"),
        ("virtual_method", "()I"),
        ("interface_method", "()I"),
        ("native_method", "()I"),
    ];

    for (name, descriptor) in invocation_types {
        group.bench_with_input(BenchmarkId::from_parameter(name), descriptor, |b, _| {
            b.iter(|| {
                let mut interpreter = Interpreter::new();
                // Warm up JIT
                for _ in 0..100 {
                    let _ = interpreter.invoke_method("com/example/Benchmarks", name, &[]);
                }
                // Measure
                black_box(
                    interpreter
                        .invoke_method("com/example/Benchmarks", name, &[])
                        .ok(),
                )
            });
        });
    }

    group.finish();
}

/// Benchmark arithmetic operations
fn benchmark_arithmetic(c: &mut Criterion) {
    let mut group = c.benchmark_group("arithmetic");

    let operations = vec![
        ("iadd", |i| i + i),
        ("imul", |i| i * i),
        ("idiv", |i| i / 2),
        ("ishl", |i| i << 1),
        ("iand", |i| i & 0xFF),
    ];

    for (name, op) in operations {
        group.bench_with_input(BenchmarkId::from_parameter(name), name, |b, _| {
            b.iter(|| {
                let mut result = 0i32;
                for i in 0..1000 {
                    result = black_box(op(result.wrapping_add(i)));
                }
                black_box(result)
            });
        });
    }

    group.finish();
}

/// Benchmark memory operations
fn benchmark_memory(c: &mut Criterion) {
    let mut group = c.benchmark_group("memory");

    // Array operations
    for size in [10, 100, 1000, 10000].iter() {
        group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| {
            b.iter(|| {
                let mut interpreter = Interpreter::new();

                // Allocate array
                let arr = interpreter.new_array_int(size)?;

                // Write to array
                for i in 0..size {
                    interpreter.array_set_int(arr, i, i as i32)?;
                }

                // Read from array
                let mut sum = 0;
                for i in 0..size {
                    sum += interpreter.array_get_int(arr, i)?;
                }

                black_box(sum)
            });
        });
    }

    // Object allocation
    group.bench_function("object_allocation", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            for _ in 0..1000 {
                black_box(interpreter.new_instance("java/lang/Object").ok());
            }
        });
    });

    // Field access
    group.bench_function("field_access", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            let obj = interpreter.new_instance("com/example/Data")?;
            for i in 0..1000 {
                interpreter.set_field_value(obj, "value", jvmrs::Value::Int(i))?;
                black_box(interpreter.get_field_value(obj, "value")?);
            }
        });
    });

    group.finish();
}

/// Benchmark garbage collection
fn benchmark_gc(c: &mut Criterion) {
    let mut group = c.benchmark_group("garbage_collection");

    for alloc_count in [100, 1000, 10000].iter() {
        group.bench_with_input(
            BenchmarkId::from_parameter(alloc_count),
            alloc_count,
            |b, &count| {
                b.iter(|| {
                    let mut interpreter = Interpreter::new();

                    // Allocate many objects
                    for _ in 0..count {
                        black_box(interpreter.new_instance("java/lang/Object").ok());
                    }

                    // Trigger GC
                    interpreter.gc();
                });
            },
        );
    }

    group.finish();
}

/// Benchmark string operations
fn benchmark_strings(c: &mut Criterion) {
    let mut group = c.benchmark_group("strings");

    group.bench_function("string_concat", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            let mut result = interpreter.new_string("".to_string())?;
            for i in 0..100 {
                let part = interpreter.new_string(format!("{}", i))?;
                result = interpreter.string_concat(result, part)?;
            }
            black_box(result)
        });
    });

    group.bench_function("string_length", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            let s = interpreter.new_string("Hello, World!")?;
            black_box(interpreter.string_length(s))
        });
    });

    group.finish();
}

/// Benchmark collection operations
fn benchmark_collections(c: &mut Criterion) {
    let mut group = c.benchmark_group("collections");

    // ArrayList operations
    let sizes = [100, 1000, 10000];
    for size in sizes.iter() {
        group.bench_with_input(
            BenchmarkId::from_parameter(format!("arraylist_add_{}", size)),
            size,
            |b, &size| {
                b.iter(|| {
                    let mut interpreter = Interpreter::new();
                    let list = interpreter.new_instance("java/util/ArrayList")?;
                    for i in 0..size {
                        interpreter.invoke_method(list, "add", &[jvmrs::Value::Int(i)])?;
                    }
                    black_box(list)
                });
            },
        );
    }

    // HashMap operations
    for size in sizes.iter() {
        group.bench_with_input(
            BenchmarkId::from_parameter(format!("hashmap_put_{}", size)),
            size,
            |b, &size| {
                b.iter(|| {
                    let mut interpreter = Interpreter::new();
                    let map = interpreter.new_instance("java/util/HashMap")?;
                    for i in 0..size {
                        interpreter.invoke_method(
                            map,
                            "put",
                            &[jvmrs::Value::Int(i), jvmrs::Value::Int(i * 2)],
                        )?;
                    }
                    black_box(map)
                });
            },
        );
    }

    group.finish();
}

/// Benchmark concurrency primitives
fn benchmark_concurrency(c: &mut Criterion) {
    let mut group = c.benchmark_group("concurrency");

    group.bench_function("monitor_enter_exit", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            let obj = interpreter.new_instance("java/lang/Object")?;
            for _ in 0..1000 {
                interpreter.monitor_enter(obj)?;
                black_box(interpreter.monitor_exit(obj)?);
            }
        });
    });

    group.finish();
}

/// Benchmark JIT compilation overhead
fn benchmark_jit(c: &mut Criterion) {
    let mut group = c.benchmark_group("jit_compilation");

    group.bench_function("jit_warmup", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            // Invoke method many times to trigger JIT
            for _ in 0..1000 {
                black_box(
                    interpreter
                        .invoke_method("com/example/HotMethod", "compute", &[jvmrs::Value::Int(42)])
                        .ok(),
                );
            }
        });
    });

    group.finish();
}

/// Benchmark reflection operations
fn benchmark_reflection(c: &mut Criterion) {
    let mut group = c.benchmark_group("reflection");

    group.bench_function("get_class", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            let obj = interpreter.new_instance("java/lang/String")?;
            black_box(interpreter.get_object_class(obj))
        });
    });

    group.bench_function("get_declared_fields", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            black_box(interpreter.get_class_fields("java/lang/String"))
        });
    });

    group.bench_function("get_declared_methods", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            black_box(interpreter.get_class_methods("java/lang/String"))
        });
    });

    group.finish();
}

/// Benchmark inline caching effectiveness
fn benchmark_inline_cache(c: &mut Criterion) {
    let mut group = c.benchmark_group("inline_cache");

    // Monomorphic callsite (same receiver type)
    group.bench_function("monomorphic", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            let obj = interpreter.new_instance("com/example/Mono")?;
            for _ in 0..1000 {
                black_box(interpreter.invoke_method(obj, "method", &[]).ok());
            }
        });
    });

    // Polymorphic callsite (few receiver types)
    group.bench_function("polymorphic", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            let obj1 = interpreter.new_instance("com/example/Poly1")?;
            let obj2 = interpreter.new_instance("com/example/Poly2")?;
            for i in 0..1000 {
                let obj = if i % 2 == 0 { obj1 } else { obj2 };
                black_box(interpreter.invoke_method(obj, "method", &[]).ok());
            }
        });
    });

    group.finish();
}

/// Benchmark polyglot interop performance
fn benchmark_polyglot(c: &mut Criterion) {
    let mut group = c.benchmark_group("polyglot_interop");

    group.bench_function("rust_call_java", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            for _ in 0..100 {
                black_box(
                    interpreter
                        .invoke_method("com/example/JavaAPI", "process", &[jvmrs::Value::Int(42)])
                        .ok(),
                );
            }
        });
    });

    group.bench_function("java_call_rust", |b| {
        b.iter(|| {
            let mut interpreter = Interpreter::new();
            for _ in 0..100 {
                black_box(
                    interpreter
                        .invoke_method(
                            "com/example/RustNative",
                            "nativeMethod",
                            &[jvmrs::Value::Int(42)],
                        )
                        .ok(),
                );
            }
        });
    });

    group.finish();
}

/// Benchmark startup performance
fn benchmark_startup(c: &mut Criterion) {
    let mut group = c.benchmark_group("startup");

    group.bench_function("cold_start", |b| {
        b.iter(|| {
            let interpreter = Interpreter::new();
            black_box(interpreter.run_main("com/example/HelloWorld").ok())
        });
    });

    group.finish();
}

criterion_group!(
    benches,
    benchmark_class_loading,
    benchmark_method_invocation,
    benchmark_arithmetic,
    benchmark_memory,
    benchmark_gc,
    benchmark_strings,
    benchmark_collections,
    benchmark_concurrency,
    benchmark_jit,
    benchmark_reflection,
    benchmark_inline_cache,
    benchmark_polyglot,
    benchmark_startup
);
criterion_main!(benches);
