//! Integration tests for the tvai library

use tempfile::TempDir;
use tvai::*;

/// Test basic library initialization and configuration
#[tokio::test]
async fn test_library_initialization() {
    // Test global settings
    let settings_manager = global_settings();
    let settings = settings_manager.get_settings();
    
    assert!(settings.max_concurrent_jobs > 0);
    assert!(settings.default_quality_preset.len() > 0);
    
    // Test preset management
    let presets = global_presets();
    let preset_manager = presets.lock().unwrap();
    
    let video_presets = preset_manager.list_video_presets();
    let image_presets = preset_manager.list_image_presets();
    
    assert!(!video_presets.is_empty(), "Should have video presets");
    assert!(!image_presets.is_empty(), "Should have image presets");
    
    // Test specific presets exist
    assert!(preset_manager.get_video_preset("general_2x").is_some());
    assert!(preset_manager.get_image_preset("photo_2x").is_some());
}

/// Test GPU detection and optimization
#[tokio::test]
async fn test_gpu_detection() {
    let gpu_info = GpuManager::detect_detailed_gpu_info();
    
    // Basic GPU info should be available
    assert!(gpu_info.devices.len() > 0, "Should detect at least one GPU device");
    
    // Test GPU suitability check
    let suitable = GpuManager::is_gpu_suitable_for_ai();
    println!("GPU suitable for AI: {}", suitable);
    
    // Test GPU benchmark
    let benchmark_result = GpuManager::benchmark_gpu_performance().await;
    assert!(benchmark_result.is_ok(), "GPU benchmark should complete");
    
    let benchmark = benchmark_result.unwrap();
    assert!(benchmark.processing_time.as_millis() > 0);
    assert!(benchmark.compute_score > 0);
}

/// Test performance monitoring
#[tokio::test]
async fn test_performance_monitoring() {
    let settings = optimize_for_system();
    let mut monitor = PerformanceMonitor::new(settings);
    
    // Test operation monitoring
    let metrics = {
        let _permit = monitor.acquire_slot().await.expect("Should acquire slot");
        let operation_monitor = monitor.start_operation("test_operation", 50.0);

        // Simulate some work
        tokio::time::sleep(std::time::Duration::from_millis(10)).await;

        operation_monitor.finish(100.0)
    };

    // Record metrics after permit is dropped
    monitor.record_metrics(metrics.clone());
    
    assert_eq!(metrics.operation_type, "test_operation");
    assert_eq!(metrics.input_size_mb, 50.0);
    assert_eq!(metrics.output_size_mb, 100.0);
    assert!(metrics.processing_time.as_millis() >= 10);
    
    // Test performance summary
    let summary = monitor.get_summary();
    assert_eq!(summary.total_operations, 1);
}

/// Test error handling and user-friendly messages
#[test]
fn test_error_handling() {
    let errors = vec![
        TvaiError::TopazNotFound("/invalid/path".to_string()),
        TvaiError::FfmpegError("Test error".to_string()),
        TvaiError::InvalidParameter("Test parameter".to_string()),
        TvaiError::GpuError("Test GPU error".to_string()),
        TvaiError::UnsupportedFormat("WEBM".to_string()),
        TvaiError::InsufficientResources("Test resources".to_string()),
        TvaiError::PermissionDenied("Test permission".to_string()),
    ];
    
    for error in errors {
        // Test user-friendly messages
        let friendly_msg = error.user_friendly_message();
        assert!(!friendly_msg.is_empty(), "Should have user-friendly message");

        // Some errors have suggestions, others just return the basic message
        if matches!(error,
            TvaiError::TopazNotFound(_) |
            TvaiError::FfmpegError(_) |
            TvaiError::InvalidParameter(_) |
            TvaiError::GpuError(_) |
            TvaiError::UnsupportedFormat(_) |
            TvaiError::InsufficientResources(_) |
            TvaiError::PermissionDenied(_)
        ) {
            assert!(friendly_msg.contains("Suggestions:") || friendly_msg.contains("Suggestion:"),
                "Should contain suggestions for error: {}", error);
        }
        
        // Test error categorization
        let category = error.category();
        assert!(!category.is_empty(), "Should have error category");
        
        // Test recoverability
        let recoverable = error.is_recoverable();
        println!("Error {} is recoverable: {}", category, recoverable);
    }
}

/// Test configuration management
#[tokio::test]
async fn test_configuration_management() {
    let temp_dir = TempDir::new().expect("Should create temp dir");
    let config_path = temp_dir.path().join("test_config.toml");
    
    // Create settings manager with custom config file
    let settings_manager = SettingsManager::with_config_file(&config_path);
    
    // Update some settings
    settings_manager.set_default_use_gpu(false).expect("Should update GPU setting");
    settings_manager.set_max_concurrent_jobs(3).expect("Should update concurrent jobs");
    
    // Save settings
    settings_manager.save_to_file().expect("Should save settings");
    
    // Verify file was created
    assert!(config_path.exists(), "Config file should be created");
    
    // Load settings in new manager
    let new_manager = SettingsManager::with_config_file(&config_path);
    let loaded_settings = new_manager.get_settings();
    
    assert_eq!(loaded_settings.default_use_gpu, false);
    assert_eq!(loaded_settings.max_concurrent_jobs, 3);
}

/// Test model and parameter validation
#[test]
fn test_model_validation() {
    // Test upscale models
    let models = [
        UpscaleModel::Iris3,
        UpscaleModel::Nyx3,
        UpscaleModel::Thf4,
        UpscaleModel::Ghq5,
    ];
    
    for model in models {
        let model_str = model.as_str();
        assert!(!model_str.is_empty(), "Model should have string representation");
        
        let description = model.description();
        assert!(!description.is_empty(), "Model should have description");
        
        // Test forced scale (if any)
        if let Some(scale) = model.forces_scale() {
            assert!(scale > 0.0, "Forced scale should be positive");
        }
    }
    
    // Test interpolation models
    let interp_models = [
        InterpolationModel::Apo8,
        InterpolationModel::Chr2,
        InterpolationModel::Apf1,
        InterpolationModel::Chf3,
    ];
    
    for model in interp_models {
        let model_str = model.as_str();
        assert!(!model_str.is_empty(), "Interpolation model should have string representation");
        
        let description = model.description();
        assert!(!description.is_empty(), "Interpolation model should have description");
    }
}

/// Test image format handling
#[test]
fn test_image_formats() {
    let formats = [
        ImageFormat::Png,
        ImageFormat::Jpg,
        ImageFormat::Tiff,
        ImageFormat::Bmp,
    ];
    
    for format in formats {
        let extension = format.extension();
        assert!(!extension.is_empty(), "Format should have extension");
        
        let ffmpeg_format = format.ffmpeg_format();
        assert!(!ffmpeg_format.is_empty(), "Format should have FFmpeg format");
    }
}

/// Test parameter presets
#[test]
fn test_parameter_presets() {
    // Test video upscale presets
    let video_presets = [
        VideoUpscaleParams::for_old_video(),
        VideoUpscaleParams::for_game_content(),
        VideoUpscaleParams::for_animation(),
        VideoUpscaleParams::for_portrait(),
    ];
    
    for preset in video_presets {
        assert!(preset.scale_factor > 0.0, "Scale factor should be positive");
        assert!(preset.scale_factor <= 4.0, "Scale factor should be reasonable");
        assert!(preset.compression >= -1.0 && preset.compression <= 1.0, "Compression should be in valid range");
        assert!(preset.blend >= 0.0 && preset.blend <= 1.0, "Blend should be in valid range");
    }
    
    // Test image upscale presets
    let image_presets = [
        ImageUpscaleParams::for_photo(),
        ImageUpscaleParams::for_artwork(),
        ImageUpscaleParams::for_screenshot(),
        ImageUpscaleParams::for_portrait(),
    ];
    
    for preset in image_presets {
        assert!(preset.scale_factor > 0.0, "Scale factor should be positive");
        assert!(preset.scale_factor <= 4.0, "Scale factor should be reasonable");
        assert!(preset.compression >= -1.0 && preset.compression <= 1.0, "Compression should be in valid range");
        assert!(preset.blend >= 0.0 && preset.blend <= 1.0, "Blend should be in valid range");
    }
    
    // Test interpolation presets
    let slow_motion = InterpolationParams::for_slow_motion(30, 2.0);
    assert_eq!(slow_motion.input_fps, 30);
    assert_eq!(slow_motion.multiplier, 2.0);
    
    let animation = InterpolationParams::for_animation(24, 2.0);
    assert_eq!(animation.input_fps, 24);
    assert_eq!(animation.multiplier, 2.0);
}

/// Test utility functions
#[tokio::test]
async fn test_utility_functions() {
    // Test Topaz detection
    let topaz_path = detect_topaz_installation();
    if let Some(path) = topaz_path {
        assert!(path.exists(), "Detected Topaz path should exist");
        println!("Detected Topaz at: {}", path.display());
    } else {
        println!("Topaz Video AI not detected (this is expected in CI)");
    }
    
    // Test FFmpeg detection
    let ffmpeg_info = detect_ffmpeg();
    println!("System FFmpeg available: {}", ffmpeg_info.system_available);
    println!("Topaz FFmpeg available: {}", ffmpeg_info.topaz_available);
    
    // Test GPU detection
    let gpu_info = detect_gpu_support();
    assert!(gpu_info.available, "Should detect GPU availability");
    println!("GPU available: {}", gpu_info.available);
    println!("CUDA available: {}", gpu_info.cuda_available);
}

/// Test temporary file management
#[tokio::test]
async fn test_temp_file_management() {
    let temp_dir = TempDir::new().expect("Should create temp dir");
    let mut temp_manager = TempFileManager::new(Some(temp_dir.path())).expect("Should create temp manager");
    
    // Test temp path creation
    let temp_path1 = temp_manager.create_temp_path("test_op", "file1.txt");
    let temp_path2 = temp_manager.create_unique_temp_path("file2.txt");
    
    assert_eq!(temp_manager.file_count(), 2);
    assert!(temp_manager.is_tracked(&temp_path1));
    assert!(temp_manager.is_tracked(&temp_path2));
    
    // Create actual files
    std::fs::write(&temp_path1, "test content 1").expect("Should write file");
    std::fs::write(&temp_path2, "test content 2").expect("Should write file");
    
    assert!(temp_path1.exists());
    assert!(temp_path2.exists());
    
    // Test cleanup
    temp_manager.cleanup_operation("test_op").expect("Should cleanup operation");
    assert!(!temp_path1.exists(), "File should be cleaned up");
    assert!(temp_path2.exists(), "Other file should remain");
    
    // Test cleanup all
    temp_manager.cleanup_all().expect("Should cleanup all");
    assert!(!temp_path2.exists(), "All files should be cleaned up");
    assert_eq!(temp_manager.file_count(), 0);
}
