//! Random I/O benchmark

use std::fs::OpenOptions;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::time::Instant;

use super::super::error::ProfilingError;
use super::super::tempfile::TempFileManager;
use super::super::types::BlockSize;
use super::super::validation;

/// Random I/O result
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RandomResult {
    pub throughput_mbps: f64,
    pub iops: f64,
    pub block_size: u32,
    pub total_operations: u64,
}

/// Measure random read performance
pub fn measure_random_read(
    path: &Path,
    size_bytes: u64,
    block_size: BlockSize,
) -> Result<RandomResult, ProfilingError> {
    // Validate inputs
    validation::validate_path(path)?;
    let validated_size = validation::validate_memory_size(size_bytes, 10 * 1024 * 1024 * 1024)?; // Max 10GB
    let block_bytes = block_size.bytes() as u64;

    if block_bytes > validated_size {
        return Err(ProfilingError::InvalidConfig(
            "Block size cannot be larger than file size".to_string(),
        ));
    }

    super::super::logging::log_info(&format!(
        "Measuring random read: {} bytes, block size: {}",
        validated_size, block_bytes
    ));

    let mut manager = TempFileManager::new(path, "rand_read")?;
    let temp_path = manager.create_file(validated_size)?;

    // Fill file with data
    {
        let mut file = OpenOptions::new()
            .write(true)
            .open(&temp_path)
            .map_err(|e| {
                ProfilingError::IoError(format!("Failed to open file for writing: {}", e))
            })?;

        let buffer = vec![0u8; std::cmp::min(1024 * 1024, validated_size as usize)];
        let mut written = 0u64;
        while written < validated_size {
            let to_write = std::cmp::min(buffer.len() as u64, validated_size - written);
            file.write_all(&buffer[..to_write as usize])
                .map_err(|e| ProfilingError::IoError(format!("Failed to write file: {}", e)))?;
            written += to_write;
        }
        file.sync_all()
            .map_err(|e| ProfilingError::IoError(format!("Failed to sync file: {}", e)))?;
    }

    // Measure random read performance
    let start = Instant::now();
    let mut file = OpenOptions::new()
        .read(true)
        .open(&temp_path)
        .map_err(|e| ProfilingError::IoError(format!("Failed to open file for reading: {}", e)))?;

    let mut buffer = vec![0u8; block_bytes as usize];
    let mut total_operations = 0u64;

    let num_operations = (validated_size / block_bytes).min(10000);

    for i in 0..num_operations {
        // Use deterministic "random" for reproducibility
        let offset = ((i * 7919) % (validated_size / block_bytes)) * block_bytes;

        file.seek(SeekFrom::Start(offset))
            .map_err(|e| ProfilingError::IoError(format!("Failed to seek file: {}", e)))?;

        match file.read(&mut buffer) {
            Ok(bytes_read) if bytes_read > 0 => {
                total_operations += 1;
            }
            Ok(_) => {
                // EOF reached, stop measuring
                break;
            }
            Err(e) => {
                super::super::logging::log_warn(&format!("Read error during random I/O: {}", e));
                break;
            }
        }
    }

    let duration = start.elapsed();
    let total_bytes = total_operations * block_bytes;
    let throughput_mbps = (total_bytes as f64 / duration.as_secs_f64()) / 1_048_576.0;
    let iops = total_operations as f64 / duration.as_secs_f64();

    Ok(RandomResult {
        throughput_mbps,
        iops,
        block_size: block_size as u32,
        total_operations,
    })
}

/// Measure random write performance
pub fn measure_random_write(
    path: &Path,
    size_bytes: u64,
    block_size: BlockSize,
) -> Result<RandomResult, ProfilingError> {
    super::super::logging::log_info(&format!(
        "Measuring random write: {} bytes, block size: {}",
        size_bytes,
        block_size.bytes()
    ));

    let mut manager = TempFileManager::new(path, "rand_write")?;
    let temp_path = manager.create_file(size_bytes)?;

    let start = Instant::now();
    let mut file = OpenOptions::new().write(true).open(&temp_path)?;
    let buffer = vec![0u8; block_size.bytes()];
    let mut total_operations = 0u64;

    let num_operations = (size_bytes / block_size.bytes() as u64).min(10000);

    for _ in 0..num_operations {
        let offset = rand::random::<u64>() % (size_bytes - block_size.bytes() as u64);
        file.seek(SeekFrom::Start(offset))?;
        file.write_all(&buffer)?;
        total_operations += 1;
    }

    file.sync_all()?;
    let duration = start.elapsed();
    let total_bytes = total_operations * block_size.bytes() as u64;
    let throughput_mbps = (total_bytes as f64 / duration.as_secs_f64()) / 1_048_576.0;
    let iops = total_operations as f64 / duration.as_secs_f64();

    Ok(RandomResult {
        throughput_mbps,
        iops,
        block_size: block_size as u32,
        total_operations,
    })
}
