//! Utility functions and system detection

pub mod temp;
pub mod gpu;
pub mod performance;
pub mod model_manager;

// Re-export main types
pub use temp::TempFileManager;
pub use gpu::{GpuManager, DetailedGpuInfo, GpuDevice, GpuSettings, GpuBenchmarkResult};
pub use model_manager::{ModelManager, ModelInfo, ModelType, ValidationReport};
pub use performance::{PerformanceMonitor, PerformanceMetrics, PerformanceSettings, ProcessingMode, PerformanceSummary, optimize_for_system};

use std::path::{Path, PathBuf};
use std::time::Duration;
use serde::{Deserialize, Serialize};

use crate::core::TvaiError;
use crate::video::VideoUpscaleParams;

/// 列出可用与缺失模型（便捷函数）
pub fn list_available_models() -> Result<(Vec<ModelInfo>, Vec<String>), TvaiError> {
    if let Some(topaz) = detect_topaz_installation() {
        let mm = ModelManager::new(&topaz)?;
        let all = mm.get_all_models()?;
        let downloaded = mm.get_downloaded_models()?;
        Ok((all, downloaded))
    } else {
        Err(TvaiError::TopazNotFound("Topaz Video AI not found".into()))
    }
}

/// 校验一组所需模型并给出替代建议
pub fn validate_models(required: &[&str]) -> Result<ValidationReport, TvaiError> {
    if let Some(topaz) = detect_topaz_installation() {
        let mm = ModelManager::new(&topaz)?;
        mm.validate_models(required)
    } else {
        Err(TvaiError::TopazNotFound("Topaz Video AI not found".into()))
    }
}

/// GPU information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GpuInfo {
    pub available: bool,
    pub cuda_available: bool,
    pub opencl_available: bool,
    pub device_name: Option<String>,
    pub memory_gb: Option<f32>,
}

/// FFmpeg information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FfmpegInfo {
    pub system_available: bool,
    pub topaz_available: bool,
    pub system_version: Option<String>,
    pub topaz_version: Option<String>,
    pub system_path: Option<PathBuf>,
    pub topaz_path: Option<PathBuf>,
}

/// Video file information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VideoInfo {
    pub duration: Duration,
    pub width: u32,
    pub height: u32,
    pub fps: f32,
    pub codec: String,
    pub bitrate: Option<u64>,
    pub file_size: u64,
}

/// Image file information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageInfo {
    pub width: u32,
    pub height: u32,
    pub format: String,
    pub color_depth: u32,
    pub file_size: u64,
}

/// Detect Topaz Video AI installation
pub fn detect_topaz_installation() -> Option<PathBuf> {
    let common_paths = if cfg!(windows) {
        vec![
            PathBuf::from(r"C:\Program Files\Topaz Labs LLC\Topaz Video AI"),
            PathBuf::from(r"C:\Program Files (x86)\Topaz Labs LLC\Topaz Video AI"),
        ]
    } else if cfg!(target_os = "macos") {
        vec![
            PathBuf::from("/Applications/Topaz Video AI.app/Contents/MacOS"),
        ]
    } else {
        vec![
            PathBuf::from("/opt/topaz-video-ai"),
            PathBuf::from("/usr/local/bin/topaz-video-ai"),
        ]
    };
    
    for path in common_paths {
        if path.exists() {
            let ffmpeg_path = path.join(if cfg!(windows) { "ffmpeg.exe" } else { "ffmpeg" });
            if ffmpeg_path.exists() {
                return Some(path);
            }
        }
    }
    
    None
}

/// Detect GPU support
pub fn detect_gpu_support() -> GpuInfo {
    // This is a simplified implementation
    // In a real implementation, you would check for CUDA/OpenCL
    GpuInfo {
        available: true, // Assume available for now
        cuda_available: true,
        opencl_available: true,
        device_name: Some("Unknown GPU".to_string()),
        memory_gb: Some(8.0),
    }
}

/// Detect FFmpeg installations
pub fn detect_ffmpeg() -> FfmpegInfo {
    let topaz_path = detect_topaz_installation();
    let topaz_available = topaz_path.is_some();
    let topaz_ffmpeg_path = topaz_path.as_ref().map(|p| p.join("ffmpeg.exe"));
    
    // Check system FFmpeg
    let system_available = std::process::Command::new("ffmpeg")
        .arg("-version")
        .output()
        .map(|output| output.status.success())
        .unwrap_or(false);
    
    FfmpegInfo {
        system_available,
        topaz_available,
        system_version: None, // Would be populated by actual version check
        topaz_version: None,  // Would be populated by actual version check
        system_path: if system_available { Some(PathBuf::from("ffmpeg")) } else { None },
        topaz_path: topaz_ffmpeg_path,
    }
}

/// Get video file information
pub async fn get_video_info(path: &Path) -> Result<VideoInfo, TvaiError> {
    if !path.exists() {
        return Err(TvaiError::FileNotFound(path.display().to_string()));
    }
    
    // This would use FFprobe to get actual video information
    // For now, return placeholder data
    Ok(VideoInfo {
        duration: Duration::from_secs(60),
        width: 1920,
        height: 1080,
        fps: 30.0,
        codec: "h264".to_string(),
        bitrate: Some(5000000),
        file_size: std::fs::metadata(path)?.len(),
    })
}

/// Get image file information
pub fn get_image_info(path: &Path) -> Result<ImageInfo, TvaiError> {
    if !path.exists() {
        return Err(TvaiError::FileNotFound(path.display().to_string()));
    }
    
    // This would use an image library to get actual image information
    // For now, return placeholder data
    Ok(ImageInfo {
        width: 1920,
        height: 1080,
        format: "PNG".to_string(),
        color_depth: 24,
        file_size: std::fs::metadata(path)?.len(),
    })
}

/// Estimate processing time for video upscaling
pub fn estimate_processing_time(
    input_info: &VideoInfo,
    params: &VideoUpscaleParams,
) -> Duration {
    // Simple estimation based on resolution and duration
    let pixels = input_info.width as f64 * input_info.height as f64;
    let scale_factor = params.scale_factor as f64;
    let duration_secs = input_info.duration.as_secs_f64();
    
    // Rough estimation: 1 second per megapixel per second of video
    let estimated_secs = (pixels * scale_factor * duration_secs) / 1_000_000.0;
    
    Duration::from_secs_f64(estimated_secs.max(1.0))
}
