//! Memory stress testing

use std::time::{Duration, Instant};

use super::super::config::global_config_or_default;
use super::super::error::ProfilingError;
use super::super::validation;

/// Memory stress test result
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StressTestResult {
    pub duration: Duration,
    pub memory_allocated_bytes: u64,
    pub errors_detected: u32,
    pub peak_bandwidth_gbps: f64,
}

/// Run memory stress test with error detection
pub fn run_memory_stress_test(duration: Duration) -> Result<StressTestResult, ProfilingError> {
    // Validate duration
    if duration > Duration::from_secs(3600) {
        return Err(ProfilingError::InvalidConfig(
            "Stress test duration cannot exceed 1 hour".to_string(),
        ));
    }

    super::super::logging::log_info(&format!("Running memory stress test for {:?}", duration));

    let config = global_config_or_default();
    let buffer_size = std::cmp::min(
        config.memory_stress_allocation_limit / 10,
        100 * 1024 * 1024,
    ); // Max 100MB per buffer
    let num_buffers = std::cmp::min(
        10,
        (config.memory_stress_allocation_limit / buffer_size) as usize,
    );

    // Check for integer overflow
    let total_memory = match buffer_size.checked_mul(num_buffers as u64) {
        Some(total) => total,
        None => {
            return Err(ProfilingError::MemoryError(
                "Memory allocation size would overflow".to_string(),
            ));
        }
    };

    // Validate against configured limits
    validation::validate_memory_size(total_memory, config.memory_stress_allocation_limit)?;

    super::super::logging::log_info(&format!(
        "Allocating {} MB in {} buffers of {} MB each",
        total_memory / (1024 * 1024),
        num_buffers,
        buffer_size / (1024 * 1024)
    ));

    // Attempt allocation with error handling
    let mut buffers: Vec<Vec<u8>> = match std::panic::catch_unwind(|| {
        (0..num_buffers)
            .map(|_| vec![0u8; buffer_size as usize])
            .collect()
    }) {
        Ok(buffers) => buffers,
        Err(_) => {
            return Err(ProfilingError::MemoryError(
                "Memory allocation failed - insufficient memory".to_string(),
            ));
        }
    };

    let start = Instant::now();
    let mut errors = 0u32;
    let mut peak_bandwidth = 0.0f64;

    let sample_interval = Duration::from_secs(1);

    while start.elapsed() < duration {
        let sample_start = Instant::now();

        // Write pattern
        for (i, buffer) in buffers.iter_mut().enumerate() {
            let pattern = (i % 256) as u8;
            for byte in buffer.iter_mut() {
                *byte = pattern;
            }
        }

        // Verify pattern
        for (i, buffer) in buffers.iter().enumerate() {
            let pattern = (i % 256) as u8;
            for byte in buffer {
                if *byte != pattern {
                    errors += 1;
                    // Limit error counting to prevent overflow
                    if errors > 1_000_000 {
                        super::super::logging::log_error(
                            "Too many memory errors detected, stopping test",
                        );
                        return Ok(StressTestResult {
                            duration: start.elapsed(),
                            memory_allocated_bytes: total_memory,
                            errors_detected: errors,
                            peak_bandwidth_gbps: peak_bandwidth,
                        });
                    }
                }
            }
        }

        let sample_duration = sample_start.elapsed();
        let bytes_processed = (buffer_size * num_buffers as u64 * 2) as f64; // write + read
        let bandwidth_gbps = bytes_processed / sample_duration.as_secs_f64() / 1_000_000_000.0;
        peak_bandwidth = peak_bandwidth.max(bandwidth_gbps);

        if start.elapsed() + sample_interval < duration {
            std::thread::sleep(sample_interval);
        }
    }

    Ok(StressTestResult {
        duration: start.elapsed(),
        memory_allocated_bytes: total_memory,
        errors_detected: errors,
        peak_bandwidth_gbps: peak_bandwidth,
    })
}
