//! Rhai bindings for memory profiling functions

use super::super::error::ToRhaiError;
use rhai::{Engine, EvalAltResult};

// Wrapper functions for cleaner registration
fn measure_memory_bandwidth() -> Result<super::bandwidth::MemoryBandwidthResult, Box<EvalAltResult>>
{
    super::bandwidth::measure_memory_bandwidth().to_rhai_error()
}

fn test_memory_latency(
    size_bytes: i64,
) -> Result<super::latency::MemoryLatencyResult, Box<EvalAltResult>> {
    super::latency::test_memory_latency(size_bytes as u64).to_rhai_error()
}

fn measure_cache_performance() -> Result<super::cache::CacheResult, Box<EvalAltResult>> {
    super::cache::measure_cache_performance().to_rhai_error()
}

fn test_numa_bandwidth() -> Result<super::numa::NumaResult, Box<EvalAltResult>> {
    super::numa::test_numa_bandwidth().to_rhai_error()
}

fn run_memory_stress_test(
    duration_sec: i64,
) -> Result<super::stress::StressTestResult, Box<EvalAltResult>> {
    use std::time::Duration;
    super::stress::run_memory_stress_test(Duration::from_secs(duration_sec as u64)).to_rhai_error()
}

fn profile_memory_usage(pid: i64) -> Result<super::usage::MemoryUsageProfile, Box<EvalAltResult>> {
    super::usage::profile_memory_usage(pid as u32).to_rhai_error()
}

/// Register memory profiling module with Rhai engine
pub fn register_memory_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
    // Register MemoryBandwidthResult type
    engine.register_type_with_name::<super::bandwidth::MemoryBandwidthResult>(
        "MemoryBandwidthResult",
    );
    engine.register_get(
        "read_bandwidth_gbps",
        |result: &mut super::bandwidth::MemoryBandwidthResult| result.read_bandwidth_gbps,
    );
    engine.register_get(
        "write_bandwidth_gbps",
        |result: &mut super::bandwidth::MemoryBandwidthResult| result.write_bandwidth_gbps,
    );
    engine.register_get(
        "copy_bandwidth_gbps",
        |result: &mut super::bandwidth::MemoryBandwidthResult| result.copy_bandwidth_gbps,
    );
    engine.register_get(
        "buffer_size_bytes",
        |result: &mut super::bandwidth::MemoryBandwidthResult| result.buffer_size_bytes,
    );
    engine.register_get(
        "iterations",
        |result: &mut super::bandwidth::MemoryBandwidthResult| result.iterations,
    );

    // Register MemoryLatencyResult type
    engine.register_type_with_name::<super::latency::MemoryLatencyResult>("MemoryLatencyResult");
    engine.register_get(
        "latency_ns",
        |result: &mut super::latency::MemoryLatencyResult| result.latency_ns,
    );
    engine.register_get(
        "stride_bytes",
        |result: &mut super::latency::MemoryLatencyResult| result.stride_bytes as i64,
    );
    engine.register_get(
        "working_set_bytes",
        |result: &mut super::latency::MemoryLatencyResult| result.working_set_bytes as i64,
    );

    engine.register_fn("measure_memory_bandwidth", measure_memory_bandwidth);
    engine.register_fn("test_memory_latency", test_memory_latency);

    // Register CacheResult type
    engine.register_type::<super::cache::CacheResult>();
    engine.register_get(
        "l1_bandwidth_gbps",
        |result: &mut super::cache::CacheResult| result.l1_bandwidth_gbps,
    );
    engine.register_get(
        "l2_bandwidth_gbps",
        |result: &mut super::cache::CacheResult| result.l2_bandwidth_gbps,
    );
    engine.register_get(
        "l3_bandwidth_gbps",
        |result: &mut super::cache::CacheResult| result.l3_bandwidth_gbps,
    );
    engine.register_get("l1_latency_ns", |result: &mut super::cache::CacheResult| {
        result.l1_latency_ns
    });
    engine.register_get("l2_latency_ns", |result: &mut super::cache::CacheResult| {
        result.l2_latency_ns
    });
    engine.register_get("l3_latency_ns", |result: &mut super::cache::CacheResult| {
        result.l3_latency_ns
    });

    engine.register_fn("measure_cache_performance", measure_cache_performance);

    // Register NumaResult type
    engine.register_type::<super::numa::NumaResult>();
    engine.register_get(
        "local_bandwidth_gbps",
        |result: &mut super::numa::NumaResult| result.local_bandwidth_gbps,
    );
    engine.register_get(
        "remote_bandwidth_gbps",
        |result: &mut super::numa::NumaResult| result.remote_bandwidth_gbps,
    );
    engine.register_get(
        "local_latency_ns",
        |result: &mut super::numa::NumaResult| result.local_latency_ns,
    );
    engine.register_get(
        "remote_latency_ns",
        |result: &mut super::numa::NumaResult| result.remote_latency_ns,
    );
    engine.register_get("numa_nodes", |result: &mut super::numa::NumaResult| {
        result.numa_nodes
    });

    engine.register_fn("test_numa_bandwidth", test_numa_bandwidth);

    // Register StressTestResult type
    engine.register_type::<super::stress::StressTestResult>();
    engine.register_get(
        "duration",
        |result: &mut super::stress::StressTestResult| result.duration,
    );
    engine.register_get(
        "memory_allocated_bytes",
        |result: &mut super::stress::StressTestResult| result.memory_allocated_bytes,
    );
    engine.register_get(
        "memory_allocated_mb",
        |result: &mut super::stress::StressTestResult| result.memory_allocated_bytes / 1048576,
    );
    engine.register_get(
        "errors_detected",
        |result: &mut super::stress::StressTestResult| result.errors_detected,
    );
    engine.register_get(
        "peak_bandwidth_gbps",
        |result: &mut super::stress::StressTestResult| result.peak_bandwidth_gbps,
    );

    engine.register_fn("run_memory_stress_test", run_memory_stress_test);

    engine.register_fn("profile_memory_usage", profile_memory_usage);

    // Register parameterized benchmark functions for Rhai scripting
    engine.register_fn("benchmark_memory", measure_memory_bandwidth);

    // Wrapper functions for parameterized benchmarks with error handling
    engine.register_fn(
        "benchmark_memory_with_size",
        |size_mb: i64| -> super::bandwidth::MemoryBandwidthResult {
            use super::super::config::limits::memory::*;
            if size_mb < MIN_SIZE_MB || size_mb > MAX_SIZE_MB {
                log::error!(
                    "size_mb must be between {} and {}",
                    MIN_SIZE_MB,
                    MAX_SIZE_MB
                );
                return super::bandwidth::MemoryBandwidthResult {
                    read_bandwidth_gbps: 0.0,
                    write_bandwidth_gbps: 0.0,
                    copy_bandwidth_gbps: 0.0,
                    buffer_size_bytes: 0,
                    iterations: 0,
                };
            }
            super::bandwidth::measure_memory_bandwidth_with_params(
                size_mb as u64,
                DEFAULT_ITERATIONS as u32,
            )
            .unwrap_or_else(|_| {
                log::error!("Memory benchmark with size failed");
                super::bandwidth::MemoryBandwidthResult {
                    read_bandwidth_gbps: 0.0,
                    write_bandwidth_gbps: 0.0,
                    copy_bandwidth_gbps: 0.0,
                    buffer_size_bytes: 0,
                    iterations: 0,
                }
            })
        },
    );

    engine.register_fn(
        "benchmark_memory_latency_with_size",
        |size_mb: i64| -> super::latency::MemoryLatencyResult {
            use super::super::config::limits::memory::*;
            if size_mb < MIN_SIZE_MB || size_mb > MAX_SIZE_MB {
                log::error!(
                    "size_mb must be between {} and {}",
                    MIN_SIZE_MB,
                    MAX_SIZE_MB
                );
                return super::latency::MemoryLatencyResult {
                    latency_ns: 0.0,
                    stride_bytes: 0,
                    working_set_bytes: 0,
                };
            }
            super::latency::test_memory_latency((size_mb * 1024 * 1024) as u64).unwrap_or_else(
                |_| {
                    log::error!("Memory latency benchmark failed");
                    super::latency::MemoryLatencyResult {
                        latency_ns: 0.0,
                        stride_bytes: 0,
                        working_set_bytes: 0,
                    }
                },
            )
        },
    );

    engine.register_fn(
        "benchmark_memory_full",
        |size_mb: i64, iterations: i64| -> super::bandwidth::MemoryBandwidthResult {
            use super::super::config::limits::memory::*;
            if size_mb < MIN_SIZE_MB || size_mb > MAX_SIZE_MB {
                log::error!(
                    "size_mb must be between {} and {}",
                    MIN_SIZE_MB,
                    MAX_SIZE_MB
                );
                return super::bandwidth::MemoryBandwidthResult {
                    read_bandwidth_gbps: 0.0,
                    write_bandwidth_gbps: 0.0,
                    copy_bandwidth_gbps: 0.0,
                    buffer_size_bytes: 0,
                    iterations: 0,
                };
            }
            if iterations < MIN_ITERATIONS || iterations > MAX_ITERATIONS {
                log::error!(
                    "iterations must be between {} and {}",
                    MIN_ITERATIONS,
                    MAX_ITERATIONS
                );
                return super::bandwidth::MemoryBandwidthResult {
                    read_bandwidth_gbps: 0.0,
                    write_bandwidth_gbps: 0.0,
                    copy_bandwidth_gbps: 0.0,
                    buffer_size_bytes: 0,
                    iterations: 0,
                };
            }
            super::bandwidth::measure_memory_bandwidth_with_params(
                size_mb as u64,
                iterations as u32,
            )
            .unwrap_or_else(|_| {
                log::error!("Memory full benchmark failed");
                super::bandwidth::MemoryBandwidthResult {
                    read_bandwidth_gbps: 0.0,
                    write_bandwidth_gbps: 0.0,
                    copy_bandwidth_gbps: 0.0,
                    buffer_size_bytes: 0,
                    iterations: 0,
                }
            })
        },
    );

    Ok(())
}
