//! Enhanced GPU acceleration framework for ndimage operations
//!
//! This module provides a comprehensive GPU acceleration framework that builds
//! upon the existing backend system to provide advanced GPU compute capabilities,
//! memory management, and performance optimization for ndimage operations.

use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};

use scirs2_core::ndarray::{Array, ArrayView, Dimension};
use scirs2_core::numeric::{Float, FromPrimitive};

use crate::backend::Backend;
use crate::error::NdimageResult;

/// GPU memory pool for efficient allocation management
#[derive(Debug)]
pub struct GpuMemoryPool {
    /// Pool of pre-allocated GPU memory buffers
    buffers: Arc<Mutex<Vec<GpuBuffer>>>,
    /// Total allocated memory
    total_allocated: Arc<Mutex<usize>>,
    /// Peak memory usage
    peak_usage: Arc<Mutex<usize>>,
    /// Configuration for memory management
    config: MemoryPoolConfig,
}

#[derive(Debug, Clone)]
pub struct MemoryPoolConfig {
    /// Maximum memory pool size in bytes
    pub max_pool_size: usize,
    /// Initial buffer sizes to pre-allocate
    pub initial_buffer_sizes: Vec<usize>,
    /// Whether to use memory pooling
    pub enable_pooling: bool,
    /// Minimum buffer size for pooling
    pub min_buffer_size: usize,
}

impl Default for MemoryPoolConfig {
    fn default() -> Self {
        Self {
            max_pool_size: 2 * 1024 * 1024 * 1024, // 2GB default
            initial_buffer_sizes: vec![
                1024 * 1024,       // 1MB
                16 * 1024 * 1024,  // 16MB
                64 * 1024 * 1024,  // 64MB
                256 * 1024 * 1024, // 256MB
            ],
            enable_pooling: true,
            min_buffer_size: 1024, // 1KB minimum
        }
    }
}

/// GPU buffer representation
#[derive(Debug, Clone)]
pub struct GpuBuffer {
    /// Unique buffer identifier
    pub id: u64,
    /// Buffer size in bytes
    pub size: usize,
    /// Backend-specific buffer handle
    pub handle: GpuBufferHandle,
    /// Whether buffer is currently in use
    pub in_use: bool,
    /// Creation timestamp
    pub created_at: Instant,
    /// Last used timestamp
    pub last_used: Instant,
}

/// Backend-specific GPU buffer handle
#[derive(Debug, Clone)]
pub enum GpuBufferHandle {
    #[cfg(feature = "cuda")]
    Cuda(CudaBufferHandle),
    #[cfg(feature = "opencl")]
    OpenCL(OpenCLBufferHandle),
    #[cfg(all(target_os = "macos", feature = "metal"))]
    Metal(MetalBufferHandle),
    Placeholder,
}

#[cfg(feature = "cuda")]
#[derive(Debug, Clone)]
pub struct CudaBufferHandle {
    pub device_ptr: usize, // CUDA device pointer
    pub device_id: i32,
    pub stream: Option<usize>, // CUDA stream handle
}

#[cfg(feature = "opencl")]
#[derive(Debug, Clone)]
pub struct OpenCLBufferHandle {
    pub buffer: usize,  // OpenCL buffer object
    pub context: usize, // OpenCL context
    pub queue: usize,   // OpenCL command queue
}

#[cfg(all(target_os = "macos", feature = "metal"))]
#[derive(Debug, Clone)]
pub struct MetalBufferHandle {
    pub buffer: usize, // Metal buffer object
    pub device: usize, // Metal device
}

impl GpuMemoryPool {
    /// Create a new GPU memory pool
    pub fn new(config: MemoryPoolConfig) -> Self {
        let pool = Self {
            buffers: Arc::new(Mutex::new(Vec::new())),
            total_allocated: Arc::new(Mutex::new(0)),
            peak_usage: Arc::new(Mutex::new(0)),
            config,
        };

        // Pre-allocate initial buffers if pooling is enabled
        if pool.config.enable_pooling {
            for &size in &pool.config.initial_buffer_sizes {
                if let Err(e) = pool.pre_allocate_buffer(size) {
                    eprintln!(
                        "Warning: Failed to pre-allocate buffer of size {}: {:?}",
                        size, e
                    );
                }
            }
        }

        pool
    }

    /// Allocate a GPU buffer of the specified size
    pub fn allocate(&self, size: usize, backend: Backend) -> NdimageResult<GpuBuffer> {
        if !self.config.enable_pooling || size < self.config.min_buffer_size {
            return self.allocate_new_buffer(size, backend);
        }

        let mut buffers = self.buffers.lock().expect("Operation failed");

        // Find an available buffer of sufficient size
        for buffer in buffers.iter_mut() {
            if !buffer.in_use && buffer.size >= size {
                buffer.in_use = true;
                buffer.last_used = Instant::now();
                return Ok(buffer.clone());
            }
        }

        // No suitable buffer found, allocate new one
        drop(buffers);
        let new_buffer = self.allocate_new_buffer(size, backend)?;

        // Add to pool if within limits
        let mut buffers = self.buffers.lock().expect("Operation failed");
        let current_total = *self.total_allocated.lock().expect("Operation failed");
        if current_total + size <= self.config.max_pool_size {
            buffers.push(new_buffer.clone());
        }

        Ok(new_buffer)
    }

    /// Deallocate a GPU buffer (return to pool)
    pub fn deallocate(&self, buffer: &GpuBuffer) -> NdimageResult<()> {
        if !self.config.enable_pooling {
            return self.deallocate_immediate(buffer);
        }

        let mut buffers = self.buffers.lock().expect("Operation failed");
        for pool_buffer in buffers.iter_mut() {
            if pool_buffer.id == buffer.id {
                pool_buffer.in_use = false;
                return Ok(());
            }
        }

        // Buffer not in pool, deallocate immediately
        self.deallocate_immediate(buffer)
    }

    /// Get memory pool statistics
    pub fn get_statistics(&self) -> MemoryPoolStatistics {
        let buffers = self.buffers.lock().expect("Operation failed");
        let total_allocated = *self.total_allocated.lock().expect("Operation failed");
        let peak_usage = *self.peak_usage.lock().expect("Operation failed");

        let active_buffers = buffers.iter().filter(|b| b.in_use).count();
        let total_buffers = buffers.len();
        let total_pool_memory: usize = buffers.iter().map(|b| b.size).sum();

        MemoryPoolStatistics {
            total_allocated,
            peak_usage,
            active_buffers,
            total_buffers,
            total_pool_memory,
            fragmentation_ratio: Self::calculate_fragmentation(&buffers),
        }
    }

    fn pre_allocate_buffer(&self, size: usize) -> NdimageResult<()> {
        // This would pre-allocate buffers based on the backend
        // Implementation depends on specific GPU backend
        Ok(())
    }

    fn allocate_new_buffer(&self, size: usize, backend: Backend) -> NdimageResult<GpuBuffer> {
        let buffer_id = self.generate_buffer_id();
        let handle = self.create_buffer_handle(size, backend)?;

        let mut total_allocated = self.total_allocated.lock().expect("Operation failed");
        *total_allocated += size;

        let mut peak_usage = self.peak_usage.lock().expect("Operation failed");
        *peak_usage = (*peak_usage).max(*total_allocated);

        Ok(GpuBuffer {
            id: buffer_id,
            size,
            handle,
            in_use: true,
            created_at: Instant::now(),
            last_used: Instant::now(),
        })
    }

    fn deallocate_immediate(&self, buffer: &GpuBuffer) -> NdimageResult<()> {
        // Backend-specific deallocation
        match &buffer.handle {
            #[cfg(feature = "cuda")]
            GpuBufferHandle::Cuda(handle) => {
                self.deallocate_cuda_buffer(handle)?;
            }
            #[cfg(feature = "opencl")]
            GpuBufferHandle::OpenCL(handle) => {
                self.deallocate_opencl_buffer(handle)?;
            }
            #[cfg(all(target_os = "macos", feature = "metal"))]
            GpuBufferHandle::Metal(handle) => {
                self.deallocate_metal_buffer(handle)?;
            }
            GpuBufferHandle::Placeholder => {}
        }

        let mut total_allocated = self.total_allocated.lock().expect("Operation failed");
        *total_allocated = total_allocated.saturating_sub(buffer.size);

        Ok(())
    }

    fn create_buffer_handle(
        &self,
        size: usize,
        backend: Backend,
    ) -> NdimageResult<GpuBufferHandle> {
        match backend {
            #[cfg(feature = "cuda")]
            Backend::Cuda => {
                let handle = self.create_cuda_buffer(size)?;
                Ok(GpuBufferHandle::Cuda(handle))
            }
            #[cfg(feature = "opencl")]
            Backend::OpenCL => {
                let handle = self.create_opencl_buffer(size)?;
                Ok(GpuBufferHandle::OpenCL(handle))
            }
            #[cfg(all(target_os = "macos", feature = "metal"))]
            Backend::Metal => {
                let handle = self.create_metal_buffer(size)?;
                Ok(GpuBufferHandle::Metal(handle))
            }
            _ => Ok(GpuBufferHandle::Placeholder),
        }
    }

    #[cfg(feature = "cuda")]
    fn create_cuda_buffer(&self, size: usize) -> NdimageResult<CudaBufferHandle> {
        // CUDA buffer allocation would go here
        // This is a placeholder implementation
        Ok(CudaBufferHandle {
            device_ptr: 0,
            device_id: 0,
            stream: None,
        })
    }

    #[cfg(feature = "cuda")]
    fn deallocate_cuda_buffer(&self, handle: &CudaBufferHandle) -> NdimageResult<()> {
        // CUDA buffer deallocation would go here
        Ok(())
    }

    #[cfg(feature = "opencl")]
    fn create_opencl_buffer(&self, size: usize) -> NdimageResult<OpenCLBufferHandle> {
        // OpenCL buffer allocation would go here
        Ok(OpenCLBufferHandle {
            buffer: 0,
            context: 0,
            queue: 0,
        })
    }

    #[cfg(feature = "opencl")]
    fn deallocate_opencl_buffer(&self, handle: &OpenCLBufferHandle) -> NdimageResult<()> {
        // OpenCL buffer deallocation would go here
        Ok(())
    }

    #[cfg(all(target_os = "macos", feature = "metal"))]
    fn create_metal_buffer(&self, size: usize) -> NdimageResult<MetalBufferHandle> {
        // Metal buffer allocation would go here
        Ok(MetalBufferHandle {
            buffer: 0,
            device: 0,
        })
    }

    #[cfg(all(target_os = "macos", feature = "metal"))]
    fn deallocate_metal_buffer(&self, handle: &MetalBufferHandle) -> NdimageResult<()> {
        // Metal buffer deallocation would go here
        Ok(())
    }

    fn generate_buffer_id(&self) -> u64 {
        use std::sync::atomic::{AtomicU64, Ordering};
        static BUFFER_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
        BUFFER_ID_COUNTER.fetch_add(1, Ordering::Relaxed)
    }

    fn calculate_fragmentation(buffers: &[GpuBuffer]) -> f64 {
        if buffers.is_empty() {
            return 0.0;
        }

        let total_size: usize = buffers.iter().map(|b| b.size).sum();
        let used_size: usize = buffers.iter().filter(|b| b.in_use).map(|b| b.size).sum();

        if total_size == 0 {
            0.0
        } else {
            1.0 - (used_size as f64 / total_size as f64)
        }
    }
}

/// Memory pool usage statistics
#[derive(Debug, Clone)]
pub struct MemoryPoolStatistics {
    /// Total memory allocated by the pool
    pub total_allocated: usize,
    /// Peak memory usage
    pub peak_usage: usize,
    /// Number of active (in-use) buffers
    pub active_buffers: usize,
    /// Total number of buffers in pool
    pub total_buffers: usize,
    /// Total memory used by the pool
    pub total_pool_memory: usize,
    /// Memory fragmentation ratio (0.0 = no fragmentation, 1.0 = fully fragmented)
    pub fragmentation_ratio: f64,
}

/// GPU kernel compilation and caching system
#[derive(Debug)]
pub struct GpuKernelCache {
    /// Compiled kernel cache
    kernels: Arc<RwLock<HashMap<String, CompiledKernel>>>,
    /// Kernel compilation statistics
    stats: Arc<Mutex<KernelCacheStats>>,
}

#[derive(Debug, Clone)]
pub struct CompiledKernel {
    /// Kernel identifier
    pub id: String,
    /// Backend-specific kernel handle
    pub handle: KernelHandle,
    /// Compilation timestamp
    pub compiled_at: Instant,
    /// Last used timestamp
    pub last_used: Instant,
    /// Number of times used
    pub use_count: usize,
    /// Kernel performance statistics
    pub performance_stats: KernelPerformanceStats,
}

#[derive(Debug, Clone)]
pub enum KernelHandle {
    #[cfg(feature = "cuda")]
    Cuda(CudaKernelHandle),
    #[cfg(feature = "opencl")]
    OpenCL(OpenCLKernelHandle),
    #[cfg(all(target_os = "macos", feature = "metal"))]
    Metal(MetalKernelHandle),
    Placeholder,
}

#[cfg(feature = "cuda")]
#[derive(Debug, Clone)]
pub struct CudaKernelHandle {
    pub function: usize, // CUDA function handle
    pub module: usize,   // CUDA module handle
}

#[cfg(feature = "opencl")]
#[derive(Debug, Clone)]
pub struct OpenCLKernelHandle {
    pub kernel: usize,  // OpenCL kernel object
    pub program: usize, // OpenCL program object
}

#[cfg(all(target_os = "macos", feature = "metal"))]
#[derive(Debug, Clone)]
pub struct MetalKernelHandle {
    pub function: usize, // Metal compute function
    pub library: usize,  // Metal library
}

#[derive(Debug, Clone)]
pub struct KernelPerformanceStats {
    /// Average execution time
    pub avg_execution_time: Duration,
    /// Minimum execution time
    pub min_execution_time: Duration,
    /// Maximum execution time
    pub max_execution_time: Duration,
    /// Total execution time
    pub total_execution_time: Duration,
    /// Memory bandwidth achieved (GB/s)
    pub memory_bandwidth: f64,
    /// Compute utilization (0.0 - 1.0)
    pub compute_utilization: f64,
}

impl Default for KernelPerformanceStats {
    fn default() -> Self {
        Self {
            avg_execution_time: Duration::ZERO,
            min_execution_time: Duration::MAX,
            max_execution_time: Duration::ZERO,
            total_execution_time: Duration::ZERO,
            memory_bandwidth: 0.0,
            compute_utilization: 0.0,
        }
    }
}

#[derive(Debug, Clone)]
pub struct KernelCacheStats {
    /// Number of cache hits
    pub cache_hits: usize,
    /// Number of cache misses
    pub cache_misses: usize,
    /// Number of kernels compiled
    pub kernels_compiled: usize,
    /// Total compilation time
    pub total_compilation_time: Duration,
}

impl Default for KernelCacheStats {
    fn default() -> Self {
        Self {
            cache_hits: 0,
            cache_misses: 0,
            kernels_compiled: 0,
            total_compilation_time: Duration::ZERO,
        }
    }
}

impl GpuKernelCache {
    /// Create a new kernel cache
    pub fn new() -> Self {
        Self {
            kernels: Arc::new(RwLock::new(HashMap::new())),
            stats: Arc::new(Mutex::new(KernelCacheStats::default())),
        }
    }

    /// Get or compile a kernel
    pub fn get_or_compile_kernel(
        &self,
        kernel_id: &str,
        kernel_source: &str,
        backend: Backend,
        compile_options: &[String],
    ) -> NdimageResult<CompiledKernel> {
        // Check cache first
        {
            let kernels = self.kernels.read().expect("Operation failed");
            if let Some(kernel) = kernels.get(kernel_id) {
                let mut stats = self.stats.lock().expect("Operation failed");
                stats.cache_hits += 1;

                // Update usage statistics
                let mut updated_kernel = kernel.clone();
                updated_kernel.last_used = Instant::now();
                updated_kernel.use_count += 1;

                return Ok(updated_kernel);
            }
        }

        // Cache miss, compile kernel
        let mut stats = self.stats.lock().expect("Operation failed");
        stats.cache_misses += 1;
        let compilation_start = Instant::now();

        let kernel_handle = self.compile_kernel(kernel_source, backend, compile_options)?;

        let compilation_time = compilation_start.elapsed();
        stats.kernels_compiled += 1;
        stats.total_compilation_time += compilation_time;
        drop(stats);

        let compiled_kernel = CompiledKernel {
            id: kernel_id.to_string(),
            handle: kernel_handle,
            compiled_at: Instant::now(),
            last_used: Instant::now(),
            use_count: 1,
            performance_stats: KernelPerformanceStats::default(),
        };

        // Store in cache
        {
            let mut kernels = self.kernels.write().expect("Operation failed");
            kernels.insert(kernel_id.to_string(), compiled_kernel.clone());
        }

        Ok(compiled_kernel)
    }

    /// Update kernel performance statistics
    pub fn update_kernel_stats(
        &self,
        kernel_id: &str,
        execution_time: Duration,
        memory_bandwidth: f64,
        compute_utilization: f64,
    ) -> NdimageResult<()> {
        let mut kernels = self.kernels.write().expect("Operation failed");
        if let Some(kernel) = kernels.get_mut(kernel_id) {
            let stats = &mut kernel.performance_stats;

            // Update timing statistics
            stats.total_execution_time += execution_time;
            stats.min_execution_time = stats.min_execution_time.min(execution_time);
            stats.max_execution_time = stats.max_execution_time.max(execution_time);
            stats.avg_execution_time = stats.total_execution_time / kernel.use_count as u32;

            // Update performance metrics (using exponential moving average)
            let alpha = 0.1; // Smoothing factor
            stats.memory_bandwidth =
                alpha * memory_bandwidth + (1.0 - alpha) * stats.memory_bandwidth;
            stats.compute_utilization =
                alpha * compute_utilization + (1.0 - alpha) * stats.compute_utilization;
        }

        Ok(())
    }

    /// Get cache statistics
    pub fn get_cache_stats(&self) -> KernelCacheStats {
        self.stats.lock().expect("Operation failed").clone()
    }

    /// Clear the kernel cache
    pub fn clear_cache(&self) {
        let mut kernels = self.kernels.write().expect("Operation failed");
        kernels.clear();

        let mut stats = self.stats.lock().expect("Operation failed");
        *stats = KernelCacheStats::default();
    }

    fn compile_kernel(
        &self,
        source: &str,
        backend: Backend,
        options: &[String],
    ) -> NdimageResult<KernelHandle> {
        match backend {
            #[cfg(feature = "cuda")]
            Backend::Cuda => {
                let handle = self.compile_cuda_kernel(source, options)?;
                Ok(KernelHandle::Cuda(handle))
            }
            #[cfg(feature = "opencl")]
            Backend::OpenCL => {
                let handle = self.compile_opencl_kernel(source, options)?;
                Ok(KernelHandle::OpenCL(handle))
            }
            #[cfg(all(target_os = "macos", feature = "metal"))]
            Backend::Metal => {
                let handle = self.compile_metal_kernel(source, options)?;
                Ok(KernelHandle::Metal(handle))
            }
            _ => Ok(KernelHandle::Placeholder),
        }
    }

    #[cfg(feature = "cuda")]
    fn compile_cuda_kernel(
        &self,
        source: &str,
        options: &[String],
    ) -> NdimageResult<CudaKernelHandle> {
        // CUDA kernel compilation would go here
        Ok(CudaKernelHandle {
            function: 0,
            module: 0,
        })
    }

    #[cfg(feature = "opencl")]
    fn compile_opencl_kernel(
        &self,
        source: &str,
        options: &[String],
    ) -> NdimageResult<OpenCLKernelHandle> {
        // OpenCL kernel compilation would go here
        Ok(OpenCLKernelHandle {
            kernel: 0,
            program: 0,
        })
    }

    #[cfg(all(target_os = "macos", feature = "metal"))]
    fn compile_metal_kernel(
        &self,
        source: &str,
        options: &[String],
    ) -> NdimageResult<MetalKernelHandle> {
        // Metal kernel compilation would go here
        Ok(MetalKernelHandle {
            function: 0,
            library: 0,
        })
    }
}

/// High-level GPU acceleration manager
pub struct GpuAccelerationManager {
    /// Memory pool for GPU buffers
    memory_pool: GpuMemoryPool,
    /// Kernel cache for compiled GPU kernels
    kernel_cache: GpuKernelCache,
    /// Device manager for hardware detection
    device_manager: crate::backend::DeviceManager,
    /// Performance profiler
    profiler: Arc<Mutex<GpuProfiler>>,
}

#[derive(Debug)]
pub struct GpuProfiler {
    /// Operation timing history
    timinghistory: Vec<(String, Duration)>,
    /// Memory usage history
    memoryhistory: Vec<(Instant, usize)>,
    /// Performance metrics
    metrics: GpuPerformanceMetrics,
}

#[derive(Debug, Clone)]
pub struct GpuPerformanceMetrics {
    /// Total GPU operations performed
    pub total_operations: usize,
    /// Total GPU execution time
    pub total_gpu_time: Duration,
    /// Average memory bandwidth
    pub avg_memory_bandwidth: f64,
    /// GPU utilization percentage
    pub gpu_utilization: f64,
    /// Memory efficiency (used/allocated)
    pub memory_efficiency: f64,
}

impl Default for GpuPerformanceMetrics {
    fn default() -> Self {
        Self {
            total_operations: 0,
            total_gpu_time: Duration::ZERO,
            avg_memory_bandwidth: 0.0,
            gpu_utilization: 0.0,
            memory_efficiency: 0.0,
        }
    }
}

impl GpuAccelerationManager {
    /// Create a new GPU acceleration manager
    pub fn new(config: MemoryPoolConfig) -> NdimageResult<Self> {
        Ok(Self {
            memory_pool: GpuMemoryPool::new(config),
            kernel_cache: GpuKernelCache::new(),
            device_manager: crate::backend::DeviceManager::new()?,
            profiler: Arc::new(Mutex::new(GpuProfiler {
                timinghistory: Vec::new(),
                memoryhistory: Vec::new(),
                metrics: GpuPerformanceMetrics::default(),
            })),
        })
    }

    /// Execute an operation on the GPU with automatic memory management
    pub fn execute_operation<T, D>(
        &self,
        operation_name: &str,
        input: ArrayView<T, D>,
        kernel_source: &str,
        backend: Backend,
    ) -> NdimageResult<Array<T, D>>
    where
        T: Float + FromPrimitive + Clone + Send + Sync,
        D: Dimension,
    {
        let start_time = Instant::now();

        // Calculate memory requirements
        let input_size = input.len() * std::mem::size_of::<T>();
        let output_size = input_size; // Assume same size output for simplicity
        let total_memory_needed = input_size + output_size;

        // Allocate GPU buffers
        let input_buffer = self.memory_pool.allocate(input_size, backend)?;
        let output_buffer = self.memory_pool.allocate(output_size, backend)?;

        // Get or compile kernel
        let kernel = self.kernel_cache.get_or_compile_kernel(
            operation_name,
            kernel_source,
            backend,
            &[], // Default compile options
        )?;

        // Execute operation (placeholder - would be backend-specific)
        let result =
            self.execute_kernel_operation(&kernel, &input, &input_buffer, &output_buffer)?;

        // Clean up buffers
        self.memory_pool.deallocate(&input_buffer)?;
        self.memory_pool.deallocate(&output_buffer)?;

        // Update profiling statistics
        let execution_time = start_time.elapsed();
        self.update_profiling_stats(operation_name, execution_time, total_memory_needed)?;

        Ok(result)
    }

    /// Get comprehensive performance report
    pub fn get_performance_report(&self) -> GpuPerformanceReport {
        let memory_stats = self.memory_pool.get_statistics();
        let cache_stats = self.kernel_cache.get_cache_stats();
        let profiler = self.profiler.lock().expect("Operation failed");

        GpuPerformanceReport {
            memory_statistics: memory_stats,
            cache_statistics: cache_stats,
            performancemetrics: profiler.metrics.clone(),
            recommendations: self.generate_performance_recommendations(),
        }
    }

    fn execute_kernel_operation<T, D>(
        &self,
        kernel: &CompiledKernel,
        input: &ArrayView<T, D>,
        input_buffer: &GpuBuffer,
        output_buffer: &GpuBuffer,
    ) -> NdimageResult<Array<T, D>>
    where
        T: Float + FromPrimitive + Clone,
        D: Dimension,
    {
        // This would contain the actual kernel execution logic
        // For now, return a placeholder result
        Ok(Array::zeros(input.raw_dim()))
    }

    fn update_profiling_stats(
        &self,
        operation_name: &str,
        execution_time: Duration,
        memory_used: usize,
    ) -> NdimageResult<()> {
        let mut profiler = self.profiler.lock().expect("Operation failed");

        profiler
            .timinghistory
            .push((operation_name.to_string(), execution_time));
        profiler.memoryhistory.push((Instant::now(), memory_used));

        // Update metrics
        profiler.metrics.total_operations += 1;
        profiler.metrics.total_gpu_time += execution_time;

        // Calculate moving averages
        if profiler.timinghistory.len() > 1 {
            let avg_time =
                profiler.metrics.total_gpu_time / profiler.metrics.total_operations as u32;
            // Update other metrics based on timing and memory history
        }

        Ok(())
    }

    fn generate_performance_recommendations(&self) -> Vec<String> {
        let mut recommendations = Vec::new();

        let memory_stats = self.memory_pool.get_statistics();
        let cache_stats = self.kernel_cache.get_cache_stats();

        // Memory recommendations
        if memory_stats.fragmentation_ratio > 0.3 {
            recommendations.push(
                "High memory fragmentation detected. Consider defragmenting GPU memory pool."
                    .to_string(),
            );
        }

        if memory_stats.peak_usage > memory_stats.total_pool_memory {
            recommendations.push(
                "Memory usage exceeded pool size. Consider increasing pool size.".to_string(),
            );
        }

        // Cache recommendations
        let cache_hit_ratio = cache_stats.cache_hits as f64
            / (cache_stats.cache_hits + cache_stats.cache_misses) as f64;
        if cache_hit_ratio < 0.7 {
            recommendations.push(
                "Low kernel cache hit ratio. Consider pre-compiling frequently used kernels."
                    .to_string(),
            );
        }

        // Performance recommendations
        if recommendations.is_empty() {
            recommendations.push("GPU acceleration is performing optimally.".to_string());
        }

        recommendations
    }
}

/// Comprehensive GPU performance report
#[derive(Debug, Clone)]
pub struct GpuPerformanceReport {
    /// Memory pool statistics
    pub memory_statistics: MemoryPoolStatistics,
    /// Kernel cache statistics  
    pub cache_statistics: KernelCacheStats,
    /// Overall performance metrics
    pub performancemetrics: GpuPerformanceMetrics,
    /// Performance optimization recommendations
    pub recommendations: Vec<String>,
}

impl GpuPerformanceReport {
    /// Display the performance report
    pub fn display(&self) {
        println!("\n=== GPU Performance Report ===\n");

        println!("Memory Statistics:");
        println!(
            "  Total Allocated: {} MB",
            self.memory_statistics.total_allocated / (1024 * 1024)
        );
        println!(
            "  Peak Usage: {} MB",
            self.memory_statistics.peak_usage / (1024 * 1024)
        );
        println!(
            "  Active Buffers: {}",
            self.memory_statistics.active_buffers
        );
        println!(
            "  Fragmentation: {:.2}%",
            self.memory_statistics.fragmentation_ratio * 100.0
        );

        println!("\nKernel Cache Statistics:");
        println!("  Cache Hits: {}", self.cache_statistics.cache_hits);
        println!("  Cache Misses: {}", self.cache_statistics.cache_misses);
        println!(
            "  Hit Ratio: {:.2}%",
            (self.cache_statistics.cache_hits as f64
                / (self.cache_statistics.cache_hits + self.cache_statistics.cache_misses).max(1)
                    as f64)
                * 100.0
        );

        println!("\nPerformance Metrics:");
        println!(
            "  Total Operations: {}",
            self.performancemetrics.total_operations
        );
        println!(
            "  Total GPU Time: {:.3}ms",
            self.performancemetrics.total_gpu_time.as_secs_f64() * 1000.0
        );
        println!(
            "  GPU Utilization: {:.2}%",
            self.performancemetrics.gpu_utilization * 100.0
        );

        if !self.recommendations.is_empty() {
            println!("\nRecommendations:");
            for (i, rec) in self.recommendations.iter().enumerate() {
                println!("  {}. {}", i + 1, rec);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_memory_pool_creation() {
        let config = MemoryPoolConfig::default();
        let pool = GpuMemoryPool::new(config);

        let stats = pool.get_statistics();
        assert_eq!(stats.active_buffers, 0);
    }

    #[test]
    fn test_kernel_cache_creation() {
        let cache = GpuKernelCache::new();
        let stats = cache.get_cache_stats();

        assert_eq!(stats.cache_hits, 0);
        assert_eq!(stats.cache_misses, 0);
    }

    #[test]
    fn test_gpu_acceleration_manager_creation() {
        let config = MemoryPoolConfig::default();
        let result = GpuAccelerationManager::new(config);

        // This test might fail in environments without GPU support
        // but it verifies the basic structure
        assert!(result.is_ok() || result.is_err());
    }
}
