//! GPU detection and management utilities

use std::process::Command;
use serde::{Deserialize, Serialize};
use crate::core::TvaiError;

/// Detailed GPU information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailedGpuInfo {
    pub available: bool,
    pub cuda_available: bool,
    pub opencl_available: bool,
    pub vulkan_available: bool,
    pub devices: Vec<GpuDevice>,
    pub recommended_settings: GpuSettings,
}

/// Individual GPU device information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GpuDevice {
    pub name: String,
    pub vendor: String,
    pub memory_mb: Option<u64>,
    pub compute_capability: Option<String>,
    pub driver_version: Option<String>,
    pub supports_ai: bool,
}

/// Recommended GPU settings
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GpuSettings {
    pub use_gpu: bool,
    pub preferred_device: Option<usize>,
    pub memory_limit_mb: Option<u64>,
    pub concurrent_streams: u32,
}

impl Default for GpuSettings {
    fn default() -> Self {
        Self {
            use_gpu: true,
            preferred_device: None,
            memory_limit_mb: None,
            concurrent_streams: 1,
        }
    }
}

/// GPU detection and management
pub struct GpuManager;

impl GpuManager {
    /// Detect detailed GPU information
    pub fn detect_detailed_gpu_info() -> DetailedGpuInfo {
        let mut info = DetailedGpuInfo {
            available: false,
            cuda_available: false,
            opencl_available: false,
            vulkan_available: false,
            devices: Vec::new(),
            recommended_settings: GpuSettings::default(),
        };

        // Detect CUDA
        info.cuda_available = Self::detect_cuda();

        // Detect OpenCL
        info.opencl_available = Self::detect_opencl();

        // Detect Vulkan
        info.vulkan_available = Self::detect_vulkan();

        // Detect GPU devices
        info.devices = Self::detect_gpu_devices();

        // Set availability
        info.available = info.cuda_available || info.opencl_available || !info.devices.is_empty();

        // Generate recommendations
        info.recommended_settings = Self::generate_recommendations(&info);

        info
    }

    /// Detect CUDA availability
    fn detect_cuda() -> bool {
        // Try to run nvidia-smi
        if let Ok(output) = Command::new("nvidia-smi").arg("--query-gpu=name").arg("--format=csv,noheader").output() {
            return output.status.success() && !output.stdout.is_empty();
        }

        // Try to check for CUDA runtime
        if let Ok(output) = Command::new("nvcc").arg("--version").output() {
            return output.status.success();
        }

        false
    }

    /// Detect OpenCL availability
    fn detect_opencl() -> bool {
        // This is a simplified check
        // In a real implementation, you would use OpenCL libraries
        cfg!(target_os = "windows") || cfg!(target_os = "linux") || cfg!(target_os = "macos")
    }

    /// Detect Vulkan availability
    fn detect_vulkan() -> bool {
        // Try to check for Vulkan
        if let Ok(output) = Command::new("vulkaninfo").arg("--summary").output() {
            return output.status.success();
        }

        false
    }

    /// Detect GPU devices
    fn detect_gpu_devices() -> Vec<GpuDevice> {
        let mut devices = Vec::new();

        // Try to get NVIDIA GPU info
        if let Ok(output) = Command::new("nvidia-smi")
            .arg("--query-gpu=name,memory.total,driver_version")
            .arg("--format=csv,noheader,nounits")
            .output()
        {
            if output.status.success() {
                let output_str = String::from_utf8_lossy(&output.stdout);
                for line in output_str.lines() {
                    if let Some(device) = Self::parse_nvidia_device_info(line) {
                        devices.push(device);
                    }
                }
            }
        }

        // Try to get AMD GPU info (simplified)
        if devices.is_empty() {
            // Fallback: assume integrated graphics
            devices.push(GpuDevice {
                name: "Integrated Graphics".to_string(),
                vendor: "Unknown".to_string(),
                memory_mb: None,
                compute_capability: None,
                driver_version: None,
                supports_ai: false,
            });
        }

        devices
    }

    /// Parse NVIDIA device information
    fn parse_nvidia_device_info(line: &str) -> Option<GpuDevice> {
        let parts: Vec<&str> = line.split(',').map(|s| s.trim()).collect();
        if parts.len() >= 3 {
            let memory_mb = parts[1].parse::<u64>().ok();

            Some(GpuDevice {
                name: parts[0].to_string(),
                vendor: "NVIDIA".to_string(),
                memory_mb,
                compute_capability: None, // Would need additional query
                driver_version: Some(parts[2].to_string()),
                supports_ai: true, // NVIDIA GPUs generally support AI workloads
            })
        } else {
            None
        }
    }

    /// Generate GPU recommendations
    fn generate_recommendations(info: &DetailedGpuInfo) -> GpuSettings {
        let mut settings = GpuSettings::default();

        if !info.available {
            settings.use_gpu = false;
            return settings;
        }

        // Find best GPU device
        let mut best_device_idx = None;
        let mut max_memory = 0;

        for (idx, device) in info.devices.iter().enumerate() {
            if device.supports_ai {
                if let Some(memory) = device.memory_mb {
                    if memory > max_memory {
                        max_memory = memory;
                        best_device_idx = Some(idx);
                    }
                }
            }
        }

        settings.preferred_device = best_device_idx;

        // Set memory limit based on available memory
        if max_memory > 0 {
            // Use 80% of available memory
            settings.memory_limit_mb = Some((max_memory as f64 * 0.8) as u64);

            // Set concurrent streams based on memory
            settings.concurrent_streams = if max_memory >= 8192 {
                2 // High-end GPU
            } else if max_memory >= 4096 {
                1 // Mid-range GPU
            } else {
                1 // Low-end GPU
            };
        }

        settings
    }

    /// Check if GPU is suitable for AI workloads
    pub fn is_gpu_suitable_for_ai() -> bool {
        let info = Self::detect_detailed_gpu_info();

        // Check if we have a suitable GPU
        info.devices.iter().any(|device| {
            device.supports_ai && device.memory_mb.unwrap_or(0) >= 2048 // At least 2GB
        })
    }

    /// Get recommended memory usage for processing
    pub fn get_recommended_memory_usage() -> Option<u64> {
        let info = Self::detect_detailed_gpu_info();
        info.recommended_settings.memory_limit_mb
    }

    /// Benchmark GPU performance (simplified)
    pub async fn benchmark_gpu_performance() -> Result<GpuBenchmarkResult, TvaiError> {
        let start_time = std::time::Instant::now();

        // This would run a simple GPU benchmark
        // For now, we'll simulate it
        tokio::time::sleep(std::time::Duration::from_millis(100)).await;

        let elapsed = start_time.elapsed();

        Ok(GpuBenchmarkResult {
            processing_time: elapsed,
            memory_bandwidth_gbps: 100.0, // Simulated
            compute_score: 1000, // Simulated
            recommended_for_ai: true,
        })
    }
}

/// GPU benchmark result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GpuBenchmarkResult {
    pub processing_time: std::time::Duration,
    pub memory_bandwidth_gbps: f64,
    pub compute_score: u32,
    pub recommended_for_ai: bool,
}
