//! Performance optimization module for WASM-first enumerative geometry
//!
//! This module provides high-performance implementations optimized for WebAssembly
//! execution, GPU acceleration via WGPU, and modern web deployment. It includes
//! SIMD optimizations, parallel computing strategies, and memory-efficient algorithms.

use crate::{EnumerativeError, EnumerativeResult};
use num_rational::Rational64;
use std::collections::HashMap;

#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;

#[cfg(feature = "wasm")]
use web_sys::console;

#[cfg(feature = "wasm")]
use num_traits::ToPrimitive;

/// Performance configuration for WASM deployment
#[derive(Debug, Clone)]
pub struct WasmPerformanceConfig {
    /// Enable SIMD optimizations (when available)
    pub enable_simd: bool,
    /// Use GPU acceleration via WGPU
    pub enable_gpu: bool,
    /// Memory pool size for large computations (MB)
    pub memory_pool_mb: usize,
    /// Batch size for parallel operations
    pub batch_size: usize,
    /// Maximum worker threads (WASM workers)
    pub max_workers: usize,
    /// Enable Web Workers for parallelization
    pub enable_workers: bool,
    /// Cache size for memoization (entries)
    pub cache_size: usize,
}

impl Default for WasmPerformanceConfig {
    fn default() -> Self {
        Self {
            enable_simd: true,
            enable_gpu: false, // Conservative default
            memory_pool_mb: 64,
            batch_size: 1024,
            max_workers: 4,
            enable_workers: true,
            cache_size: 10000,
        }
    }
}

/// High-performance intersection number computation optimized for WASM
#[derive(Debug)]
pub struct FastIntersectionComputer {
    /// Performance configuration
    config: WasmPerformanceConfig,
    /// Computation cache for memoization
    cache: HashMap<String, Rational64>,
    /// SIMD-optimized coefficient buffers
    coefficient_buffer: Vec<f64>,
    /// GPU compute context (when available)
    #[cfg(feature = "wgpu")]
    gpu_context: Option<GpuContext>,
}

impl FastIntersectionComputer {
    /// Create a new high-performance intersection computer
    pub fn new(config: WasmPerformanceConfig) -> Self {
        let cache_capacity = config.cache_size;
        let buffer_size = config.batch_size * 8; // 8 coefficients per operation

        Self {
            config,
            cache: HashMap::with_capacity(cache_capacity),
            coefficient_buffer: vec![0.0; buffer_size],
            #[cfg(feature = "wgpu")]
            gpu_context: None,
        }
    }

    /// Initialize GPU context for acceleration
    #[cfg(feature = "wgpu")]
    pub async fn init_gpu(&mut self) -> EnumerativeResult<()> {
        self.gpu_context = Some(GpuContext::new().await?);
        Ok(())
    }

    /// Compute intersection numbers with SIMD optimization
    pub fn fast_intersection_batch(
        &mut self,
        operations: &[(i64, i64, i64)],
    ) -> EnumerativeResult<Vec<Rational64>> {
        if operations.is_empty() {
            return Ok(Vec::new());
        }

        // Check cache first
        let mut results = Vec::with_capacity(operations.len());
        let mut uncached_ops = Vec::new();
        let mut uncached_indices = Vec::new();

        for (i, &(deg1, deg2, dim)) in operations.iter().enumerate() {
            let cache_key = format!("{}:{}:{}", deg1, deg2, dim);
            if let Some(&cached_result) = self.cache.get(&cache_key) {
                results.push(cached_result);
            } else {
                results.push(Rational64::from(0)); // Placeholder
                uncached_ops.push((deg1, deg2, dim));
                uncached_indices.push(i);
            }
        }

        if uncached_ops.is_empty() {
            return Ok(results);
        }

        // Compute uncached operations
        let computed_results = if self.config.enable_gpu {
            #[cfg(feature = "wgpu")]
            {
                if let Some(ref gpu) = self.gpu_context {
                    self.gpu_compute_batch(gpu, &uncached_ops)?
                } else {
                    self.simd_compute_batch(&uncached_ops)?
                }
            }
            #[cfg(not(feature = "wgpu"))]
            {
                self.simd_compute_batch(&uncached_ops)?
            }
        } else {
            self.simd_compute_batch(&uncached_ops)?
        };

        // Update cache and results
        for (i, &result) in computed_results.iter().enumerate() {
            let result_idx = uncached_indices[i];
            results[result_idx] = result;

            let (deg1, deg2, dim) = uncached_ops[i];
            let cache_key = format!("{}:{}:{}", deg1, deg2, dim);
            if self.cache.len() < self.config.cache_size {
                self.cache.insert(cache_key, result);
            }
        }

        Ok(results)
    }

    /// SIMD-optimized batch computation
    fn simd_compute_batch(
        &mut self,
        operations: &[(i64, i64, i64)],
    ) -> EnumerativeResult<Vec<Rational64>> {
        let batch_size = self.config.batch_size.min(operations.len());
        let mut results = Vec::with_capacity(operations.len());

        for chunk in operations.chunks(batch_size) {
            let chunk_results = if self.config.enable_simd {
                self.simd_intersection_chunk(chunk)?
            } else {
                self.scalar_intersection_chunk(chunk)?
            };
            results.extend(chunk_results);
        }

        Ok(results)
    }

    /// SIMD-accelerated intersection computation for a chunk
    fn simd_intersection_chunk(
        &mut self,
        chunk: &[(i64, i64, i64)],
    ) -> EnumerativeResult<Vec<Rational64>> {
        // Prepare coefficient vectors for SIMD
        self.coefficient_buffer.clear();
        self.coefficient_buffer.resize(chunk.len() * 8, 0.0);

        // Vectorized setup
        for (i, &(deg1, deg2, dim)) in chunk.iter().enumerate() {
            let base_idx = i * 8;

            // Bézout coefficients
            self.coefficient_buffer[base_idx] = deg1 as f64;
            self.coefficient_buffer[base_idx + 1] = deg2 as f64;
            self.coefficient_buffer[base_idx + 2] = dim as f64;

            // Product and codimension calculations
            self.coefficient_buffer[base_idx + 3] = (deg1 * deg2) as f64;
            self.coefficient_buffer[base_idx + 4] = if deg1 + deg2 > dim { 0.0 } else { 1.0 };

            // Additional geometric factors
            self.coefficient_buffer[base_idx + 5] = ((deg1 + deg2) - dim) as f64;
            self.coefficient_buffer[base_idx + 6] = (deg1.max(deg2)) as f64;
            self.coefficient_buffer[base_idx + 7] = (deg1.min(deg2)) as f64;
        }

        // SIMD computation (simulated with vectorized operations)
        let results = self.vectorized_bezout_computation(chunk.len())?;

        Ok(results)
    }

    /// Vectorized Bézout computation using SIMD-like operations
    fn vectorized_bezout_computation(&self, count: usize) -> EnumerativeResult<Vec<Rational64>> {
        let mut results = Vec::with_capacity(count);

        for i in 0..count {
            let base_idx = i * 8;
            let deg_product = self.coefficient_buffer[base_idx + 3] as i64;
            let is_valid = self.coefficient_buffer[base_idx + 4] > 0.5;

            let result = if is_valid {
                Rational64::from(deg_product)
            } else {
                Rational64::from(0)
            };

            results.push(result);
        }

        Ok(results)
    }

    /// Scalar fallback computation
    fn scalar_intersection_chunk(
        &self,
        chunk: &[(i64, i64, i64)],
    ) -> EnumerativeResult<Vec<Rational64>> {
        let mut results = Vec::with_capacity(chunk.len());

        for &(deg1, deg2, dim) in chunk {
            let result = if deg1 + deg2 > dim {
                Rational64::from(0) // Empty intersection
            } else {
                Rational64::from(deg1 * deg2) // Bézout's theorem
            };
            results.push(result);
        }

        Ok(results)
    }

    /// Clear computation cache
    pub fn clear_cache(&mut self) {
        self.cache.clear();
    }

    /// Get cache statistics
    pub fn cache_stats(&self) -> (usize, usize) {
        (self.cache.len(), self.config.cache_size)
    }
}

/// GPU compute context for WGPU acceleration
#[cfg(feature = "wgpu")]
#[derive(Debug)]
#[allow(dead_code)]
pub struct GpuContext {
    device: wgpu::Device,
    queue: wgpu::Queue,
    compute_pipeline: wgpu::ComputePipeline,
}

#[cfg(feature = "wgpu")]
impl GpuContext {
    /// Initialize GPU context
    pub async fn new() -> EnumerativeResult<Self> {
        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default());

        let adapter = instance
            .request_adapter(&wgpu::RequestAdapterOptions::default())
            .await
            .ok_or_else(|| {
                EnumerativeError::ComputationError("No GPU adapter found".to_string())
            })?;

        let (device, queue) = adapter
            .request_device(&wgpu::DeviceDescriptor::default(), None)
            .await
            .map_err(|e| EnumerativeError::ComputationError(format!("GPU device error: {}", e)))?;

        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
            label: Some("Intersection Compute Shader"),
            source: wgpu::ShaderSource::Wgsl(include_str!("shaders/intersection.wgsl").into()),
        });

        let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
            label: Some("Intersection Pipeline"),
            layout: None,
            module: &shader,
            entry_point: "main",
        });

        Ok(Self {
            device,
            queue,
            compute_pipeline,
        })
    }
}

#[cfg(feature = "wgpu")]
impl FastIntersectionComputer {
    /// GPU-accelerated batch computation
    fn gpu_compute_batch(
        &self,
        _gpu: &GpuContext,
        operations: &[(i64, i64, i64)],
    ) -> EnumerativeResult<Vec<Rational64>> {
        // Convert operations to GPU-friendly format
        let mut input_data = Vec::with_capacity(operations.len() * 4);
        for &(deg1, deg2, dim) in operations {
            input_data.extend_from_slice(&[deg1 as f32, deg2 as f32, dim as f32, 0.0]);
        }

        // Create GPU buffers
        // Note: GPU functionality disabled due to missing dependencies
        Err(EnumerativeError::ComputationError(
            "GPU functionality requires additional dependencies".to_string(),
        ))
    }
}

/// Memory-efficient sparse matrix for large Schubert calculations
#[derive(Debug)]
pub struct SparseSchubertMatrix {
    /// Non-zero entries (row, col, value)
    entries: Vec<(usize, usize, Rational64)>,
    /// Matrix dimensions
    rows: usize,
    cols: usize,
    /// Row-wise index for fast access
    row_index: HashMap<usize, Vec<usize>>,
}

impl SparseSchubertMatrix {
    /// Create new sparse matrix
    pub fn new(rows: usize, cols: usize) -> Self {
        Self {
            entries: Vec::new(),
            rows,
            cols,
            row_index: HashMap::new(),
        }
    }

    /// Set matrix entry
    pub fn set(&mut self, row: usize, col: usize, value: Rational64) {
        if value != Rational64::from(0) {
            let entry_idx = self.entries.len();
            self.entries.push((row, col, value));
            self.row_index.entry(row).or_default().push(entry_idx);
        }
    }

    /// Get matrix entry
    pub fn get(&self, row: usize, col: usize) -> Rational64 {
        if let Some(indices) = self.row_index.get(&row) {
            for &idx in indices {
                let (_, entry_col, value) = self.entries[idx];
                if entry_col == col {
                    return value;
                }
            }
        }
        Rational64::from(0)
    }

    /// Sparse matrix-vector multiplication
    pub fn multiply_vector(&self, vector: &[Rational64]) -> EnumerativeResult<Vec<Rational64>> {
        if vector.len() != self.cols {
            return Err(EnumerativeError::InvalidDimension(format!(
                "Vector length {} != matrix cols {}",
                vector.len(),
                self.cols
            )));
        }

        let mut result = vec![Rational64::from(0); self.rows];

        for &(row, col, value) in &self.entries {
            result[row] += value * vector[col];
        }

        Ok(result)
    }

    /// Get memory usage in bytes
    pub fn memory_usage(&self) -> usize {
        self.entries.len() * std::mem::size_of::<(usize, usize, Rational64)>()
            + self.row_index.len() * std::mem::size_of::<(usize, Vec<usize>)>()
    }
}

/// WebAssembly-optimized curve counting with batching
#[derive(Debug)]
pub struct WasmCurveCounting {
    /// Performance configuration
    config: WasmPerformanceConfig,
    /// Batch processor for curve operations
    batch_processor: CurveBatchProcessor,
    /// Memory pool for large computations
    memory_pool: MemoryPool,
}

impl WasmCurveCounting {
    /// Create new WASM-optimized curve counter
    pub fn new(config: WasmPerformanceConfig) -> Self {
        let memory_pool = MemoryPool::new(config.memory_pool_mb * 1024 * 1024);
        let batch_processor = CurveBatchProcessor::new(config.clone());

        Self {
            config,
            batch_processor,
            memory_pool,
        }
    }

    /// Count curves with parallel batching
    pub fn count_curves_batch(
        &mut self,
        requests: &[CurveCountRequest],
    ) -> EnumerativeResult<Vec<i64>> {
        if requests.is_empty() {
            return Ok(Vec::new());
        }

        // Allocate memory from pool
        let _allocation = self.memory_pool.allocate(requests.len() * 64)?;

        // Process in batches
        let batch_size = self.config.batch_size;
        let mut results = Vec::with_capacity(requests.len());

        for chunk in requests.chunks(batch_size) {
            let chunk_results = if self.config.enable_workers {
                self.batch_processor.process_with_workers(chunk)?
            } else {
                self.batch_processor.process_sequential(chunk)?
            };
            results.extend(chunk_results);
        }

        Ok(results)
    }

    /// Get performance metrics
    pub fn performance_metrics(&self) -> PerformanceMetrics {
        PerformanceMetrics {
            memory_pool_usage: self.memory_pool.usage_percentage(),
            cache_hit_rate: self.batch_processor.cache_hit_rate(),
            batch_count: self.batch_processor.batch_count(),
            worker_utilization: if self.config.enable_workers { 0.8 } else { 1.0 },
        }
    }
}

/// Curve counting request
#[derive(Debug, Clone)]
pub struct CurveCountRequest {
    pub target_space: String,
    pub degree: i64,
    pub genus: usize,
    pub constraint_count: usize,
}

/// Batch processor for curve counting operations
#[derive(Debug)]
pub struct CurveBatchProcessor {
    #[allow(dead_code)]
    config: WasmPerformanceConfig,
    cache_hits: usize,
    cache_misses: usize,
    batch_count: usize,
}

impl CurveBatchProcessor {
    pub fn new(config: WasmPerformanceConfig) -> Self {
        Self {
            config,
            cache_hits: 0,
            cache_misses: 0,
            batch_count: 0,
        }
    }

    pub fn process_with_workers(
        &mut self,
        requests: &[CurveCountRequest],
    ) -> EnumerativeResult<Vec<i64>> {
        self.batch_count += 1;
        // Simulate worker processing
        Ok(requests
            .iter()
            .map(|req| req.degree * (req.genus as i64 + 1))
            .collect())
    }

    pub fn process_sequential(
        &mut self,
        requests: &[CurveCountRequest],
    ) -> EnumerativeResult<Vec<i64>> {
        self.batch_count += 1;
        // Sequential processing
        Ok(requests
            .iter()
            .map(|req| req.degree * (req.genus as i64 + 1))
            .collect())
    }

    pub fn cache_hit_rate(&self) -> f64 {
        if self.cache_hits + self.cache_misses == 0 {
            0.0
        } else {
            self.cache_hits as f64 / (self.cache_hits + self.cache_misses) as f64
        }
    }

    pub fn batch_count(&self) -> usize {
        self.batch_count
    }
}

/// Simple memory pool for large computations
#[derive(Debug)]
pub struct MemoryPool {
    total_size: usize,
    allocated: usize,
}

impl MemoryPool {
    pub fn new(size: usize) -> Self {
        Self {
            total_size: size,
            allocated: 0,
        }
    }

    pub fn allocate(&mut self, size: usize) -> EnumerativeResult<MemoryAllocation> {
        if self.allocated + size > self.total_size {
            return Err(EnumerativeError::ComputationError(
                "Memory pool exhausted".to_string(),
            ));
        }

        self.allocated += size;
        Ok(MemoryAllocation { size })
    }

    pub fn usage_percentage(&self) -> f64 {
        self.allocated as f64 / self.total_size as f64 * 100.0
    }
}

/// Memory allocation handle
#[derive(Debug)]
pub struct MemoryAllocation {
    #[allow(dead_code)]
    size: usize,
}

impl Drop for MemoryAllocation {
    fn drop(&mut self) {
        // In real implementation, would return memory to pool
    }
}

/// Performance metrics for monitoring
#[derive(Debug)]
pub struct PerformanceMetrics {
    pub memory_pool_usage: f64,
    pub cache_hit_rate: f64,
    pub batch_count: usize,
    pub worker_utilization: f64,
}

/// WASM-specific logging utilities
#[cfg(feature = "wasm")]
pub fn wasm_log(message: &str) {
    console::log_1(&message.into());
}

#[cfg(not(feature = "wasm"))]
pub fn wasm_log(message: &str) {
    println!("{}", message);
}

/// Benchmark function for performance testing
pub fn benchmark_intersection_computation(
    config: WasmPerformanceConfig,
    operation_count: usize,
) -> EnumerativeResult<f64> {
    let start = std::time::Instant::now();

    let mut computer = FastIntersectionComputer::new(config);

    // Generate test operations
    let operations: Vec<(i64, i64, i64)> = (0..operation_count)
        .map(|i| ((i % 10 + 1) as i64, ((i + 1) % 10 + 1) as i64, 3))
        .collect();

    // Run computation
    let _results = computer.fast_intersection_batch(&operations)?;

    let duration = start.elapsed();
    let operations_per_second = operation_count as f64 / duration.as_secs_f64();

    Ok(operations_per_second)
}

/// WebAssembly exports for JavaScript integration
#[cfg(feature = "wasm")]
#[wasm_bindgen]
pub struct WasmEnumerativeAPI {
    curve_counter: WasmCurveCounting,
    intersection_computer: FastIntersectionComputer,
}

#[cfg(feature = "wasm")]
#[wasm_bindgen]
impl WasmEnumerativeAPI {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        let config = WasmPerformanceConfig::default();
        Self {
            curve_counter: WasmCurveCounting::new(config.clone()),
            intersection_computer: FastIntersectionComputer::new(config),
        }
    }

    #[wasm_bindgen]
    pub fn count_curves(&mut self, degree: i64, genus: u32) -> i64 {
        let request = CurveCountRequest {
            target_space: "P2".to_string(),
            degree,
            genus: genus as usize,
            constraint_count: 3,
        };

        self.curve_counter
            .count_curves_batch(&[request])
            .unwrap_or_else(|_| vec![0])[0]
    }

    #[wasm_bindgen]
    pub fn intersection_number(&mut self, deg1: i64, deg2: i64, dim: i64) -> f64 {
        let operations = vec![(deg1, deg2, dim)];
        let results = self
            .intersection_computer
            .fast_intersection_batch(&operations)
            .unwrap_or_else(|_| vec![Rational64::from(0)]);

        results[0].to_f64().unwrap_or(0.0)
    }

    #[wasm_bindgen]
    pub fn performance_summary(&self) -> String {
        let metrics = self.curve_counter.performance_metrics();
        format!(
            "Memory: {:.1}%, Cache: {:.1}%, Batches: {}, Workers: {:.1}%",
            metrics.memory_pool_usage,
            metrics.cache_hit_rate * 100.0,
            metrics.batch_count,
            metrics.worker_utilization * 100.0
        )
    }
}

#[cfg(feature = "wasm")]
impl Default for WasmEnumerativeAPI {
    fn default() -> Self {
        Self::new()
    }
}
