//! Performance optimization utilities

use std::time::{Duration, Instant};
use std::sync::Arc;
use tokio::sync::Semaphore;
use serde::{Deserialize, Serialize};
use crate::core::TvaiError;

/// Performance metrics for processing operations
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
    pub operation_type: String,
    pub input_size_mb: f64,
    pub output_size_mb: f64,
    pub processing_time: Duration,
    pub memory_usage_mb: Option<f64>,
    pub gpu_utilization: Option<f32>,
    pub cpu_utilization: Option<f32>,
    pub throughput_mbps: f64,
}

/// Performance optimization settings
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceSettings {
    /// Maximum concurrent operations
    pub max_concurrent_ops: usize,
    /// Memory limit in MB
    pub memory_limit_mb: Option<u64>,
    /// Enable performance monitoring
    pub enable_monitoring: bool,
    /// Preferred processing mode
    pub processing_mode: ProcessingMode,
    /// Chunk size for large files (MB)
    pub chunk_size_mb: u64,
}

/// Processing mode preferences
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ProcessingMode {
    /// Optimize for speed
    Speed,
    /// Optimize for quality
    Quality,
    /// Balance speed and quality
    Balanced,
    /// Optimize for memory usage
    MemoryEfficient,
}

impl Default for PerformanceSettings {
    fn default() -> Self {
        Self {
            max_concurrent_ops: 1,
            memory_limit_mb: None,
            enable_monitoring: true,
            processing_mode: ProcessingMode::Balanced,
            chunk_size_mb: 100,
        }
    }
}

/// Performance monitor for tracking processing metrics
pub struct PerformanceMonitor {
    settings: PerformanceSettings,
    semaphore: Arc<Semaphore>,
    metrics_history: Vec<PerformanceMetrics>,
}

impl PerformanceMonitor {
    /// Create a new performance monitor
    pub fn new(settings: PerformanceSettings) -> Self {
        let semaphore = Arc::new(Semaphore::new(settings.max_concurrent_ops));
        
        Self {
            settings,
            semaphore,
            metrics_history: Vec::new(),
        }
    }
    
    /// Acquire a processing slot (for concurrency control)
    pub async fn acquire_slot(&self) -> Result<tokio::sync::SemaphorePermit, TvaiError> {
        self.semaphore.acquire().await
            .map_err(|e| TvaiError::InsufficientResources(format!("Failed to acquire processing slot: {}", e)))
    }
    
    /// Start monitoring a processing operation
    pub fn start_operation(&self, operation_type: &str, input_size_mb: f64) -> OperationMonitor {
        OperationMonitor {
            operation_type: operation_type.to_string(),
            input_size_mb,
            start_time: Instant::now(),
            enable_monitoring: self.settings.enable_monitoring,
        }
    }
    
    /// Record completed operation metrics
    pub fn record_metrics(&mut self, metrics: PerformanceMetrics) {
        if self.settings.enable_monitoring {
            self.metrics_history.push(metrics);
            
            // Keep only recent metrics (last 100 operations)
            if self.metrics_history.len() > 100 {
                self.metrics_history.remove(0);
            }
        }
    }
    
    /// Get average processing speed for operation type
    pub fn get_average_speed(&self, operation_type: &str) -> Option<f64> {
        let relevant_metrics: Vec<_> = self.metrics_history
            .iter()
            .filter(|m| m.operation_type == operation_type)
            .collect();
        
        if relevant_metrics.is_empty() {
            return None;
        }
        
        let total_throughput: f64 = relevant_metrics.iter().map(|m| m.throughput_mbps).sum();
        Some(total_throughput / relevant_metrics.len() as f64)
    }
    
    /// Get performance recommendations
    pub fn get_recommendations(&self) -> Vec<String> {
        let mut recommendations = Vec::new();
        
        if self.metrics_history.is_empty() {
            return recommendations;
        }
        
        // Analyze recent performance
        let recent_metrics: Vec<_> = self.metrics_history.iter().rev().take(10).collect();
        
        // Check for slow processing
        let avg_throughput: f64 = recent_metrics.iter().map(|m| m.throughput_mbps).sum::<f64>() / recent_metrics.len() as f64;
        
        if avg_throughput < 1.0 {
            recommendations.push("Consider enabling GPU acceleration for better performance".to_string());
        }
        
        // Check memory usage
        if let Some(avg_memory) = self.get_average_memory_usage() {
            if avg_memory > 8000.0 {
                recommendations.push("High memory usage detected. Consider processing smaller files or reducing quality settings".to_string());
            }
        }
        
        // Check for GPU utilization
        if let Some(avg_gpu) = self.get_average_gpu_utilization() {
            if avg_gpu < 50.0 {
                recommendations.push("Low GPU utilization. Check if GPU acceleration is properly enabled".to_string());
            }
        }
        
        recommendations
    }
    
    /// Get average memory usage
    fn get_average_memory_usage(&self) -> Option<f64> {
        let memory_metrics: Vec<f64> = self.metrics_history
            .iter()
            .filter_map(|m| m.memory_usage_mb)
            .collect();
        
        if memory_metrics.is_empty() {
            None
        } else {
            Some(memory_metrics.iter().sum::<f64>() / memory_metrics.len() as f64)
        }
    }
    
    /// Get average GPU utilization
    fn get_average_gpu_utilization(&self) -> Option<f32> {
        let gpu_metrics: Vec<f32> = self.metrics_history
            .iter()
            .filter_map(|m| m.gpu_utilization)
            .collect();
        
        if gpu_metrics.is_empty() {
            None
        } else {
            Some(gpu_metrics.iter().sum::<f32>() / gpu_metrics.len() as f32)
        }
    }
    
    /// Get performance summary
    pub fn get_summary(&self) -> PerformanceSummary {
        PerformanceSummary {
            total_operations: self.metrics_history.len(),
            average_throughput_mbps: self.get_average_speed("").unwrap_or(0.0),
            average_memory_usage_mb: self.get_average_memory_usage(),
            average_gpu_utilization: self.get_average_gpu_utilization(),
            recommendations: self.get_recommendations(),
        }
    }
}

/// Individual operation monitor
pub struct OperationMonitor {
    operation_type: String,
    input_size_mb: f64,
    start_time: Instant,
    #[allow(dead_code)]
    enable_monitoring: bool,
}

impl OperationMonitor {
    /// Finish monitoring and create metrics
    pub fn finish(self, output_size_mb: f64) -> PerformanceMetrics {
        let processing_time = self.start_time.elapsed();
        let throughput_mbps = if processing_time.as_secs_f64() > 0.0 {
            self.input_size_mb / processing_time.as_secs_f64()
        } else {
            0.0
        };
        
        PerformanceMetrics {
            operation_type: self.operation_type,
            input_size_mb: self.input_size_mb,
            output_size_mb,
            processing_time,
            memory_usage_mb: None, // Would be populated by actual monitoring
            gpu_utilization: None, // Would be populated by actual monitoring
            cpu_utilization: None, // Would be populated by actual monitoring
            throughput_mbps,
        }
    }
}

/// Performance summary
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceSummary {
    pub total_operations: usize,
    pub average_throughput_mbps: f64,
    pub average_memory_usage_mb: Option<f64>,
    pub average_gpu_utilization: Option<f32>,
    pub recommendations: Vec<String>,
}

/// Optimize processing parameters based on system capabilities
pub fn optimize_for_system() -> PerformanceSettings {
    let mut settings = PerformanceSettings::default();
    
    // Detect system capabilities
    let gpu_info = crate::utils::detect_gpu_support();
    
    // Adjust based on GPU availability
    if gpu_info.available && gpu_info.cuda_available {
        settings.max_concurrent_ops = 2; // Can handle more with GPU
        settings.processing_mode = ProcessingMode::Speed;
    } else {
        settings.max_concurrent_ops = 1; // Conservative for CPU-only
        settings.processing_mode = ProcessingMode::MemoryEfficient;
    }
    
    // Adjust based on available memory (simplified)
    if let Some(memory_gb) = gpu_info.memory_gb {
        if memory_gb >= 8.0 {
            settings.chunk_size_mb = 200; // Larger chunks for high-memory systems
        } else if memory_gb >= 4.0 {
            settings.chunk_size_mb = 100;
        } else {
            settings.chunk_size_mb = 50; // Smaller chunks for low-memory systems
        }
    }
    
    settings
}
