// Integration tests for platform-config multi-source schema resolution

use std::fs;
use std::path::PathBuf;

use platform_config::{deployment::load_deployment_mode, ExtensionSchemaCache, PlatformStartup};
use tempfile::TempDir;

#[test]
fn test_deployment_mode_load() {
    // This test requires deployment-mode.ncl to exist
    match load_deployment_mode() {
        Ok(config) => {
            assert!(
                config.config.is_object(),
                "Deployment config should be a JSON object"
            );
            println!("✓ Deployment mode loaded successfully");
        }
        Err(e) => {
            eprintln!("ℹ Deployment mode not found (expected in CI): {}", e);
        }
    }
}

#[test]
fn test_platform_startup_initialization() {
    match load_deployment_mode() {
        Ok(config) => {
            let startup = PlatformStartup::new(&config.config);
            assert!(
                startup.is_ok(),
                "PlatformStartup initialization should succeed"
            );

            let startup = startup.unwrap();
            let enabled = startup.enabled_services();
            println!("✓ Enabled services: {:?}", enabled);

            // Should have at least vault_service and orchestrator
            assert!(
                !enabled.is_empty(),
                "At least one service should be enabled"
            );
        }
        Err(e) => {
            eprintln!("ℹ Deployment mode not found (expected in CI): {}", e);
        }
    }
}

#[test]
fn test_service_dependencies_validation() {
    match load_deployment_mode() {
        Ok(config) => {
            let startup = PlatformStartup::new(&config.config);
            assert!(startup.is_ok(), "PlatformStartup should initialize");

            let startup = startup.unwrap();

            // Validate orchestrator dependencies (should depend on vault_service)
            let result = startup.validate_dependencies("orchestrator");

            // This will pass if vault_service is enabled, or fail if not
            // Both are valid outcomes depending on deployment config
            match result {
                Ok(()) => println!("✓ Orchestrator dependencies validated"),
                Err(e) => println!("ℹ Orchestrator dependency check: {}", e),
            }
        }
        Err(e) => {
            eprintln!("ℹ Deployment mode not found (expected in CI): {}", e);
        }
    }
}

#[test]
fn test_startup_order_calculation() {
    match load_deployment_mode() {
        Ok(config) => {
            let startup = PlatformStartup::new(&config.config);
            assert!(startup.is_ok(), "PlatformStartup should initialize");

            let startup = startup.unwrap();
            let order = startup.get_startup_order();
            assert!(order.is_ok(), "Startup order calculation should succeed");

            let order = order.unwrap();
            println!("✓ Startup order: {:?}", order);

            // vault_service should come before orchestrator (if both enabled)
            let vault_idx = order.iter().position(|s| s == "vault_service");
            let orch_idx = order.iter().position(|s| s == "orchestrator");

            if let (Some(v), Some(o)) = (vault_idx, orch_idx) {
                assert!(v < o, "vault_service should start before orchestrator");
            }
        }
        Err(e) => {
            eprintln!("ℹ Deployment mode not found (expected in CI): {}", e);
        }
    }
}

#[test]
fn test_extension_schema_cache_creation() {
    let temp_dir = TempDir::new().unwrap();
    let cache = ExtensionSchemaCache::new(temp_dir.path().to_path_buf());

    assert!(
        cache.is_ok(),
        "ExtensionSchemaCache should create successfully"
    );
    println!("✓ Extension schema cache created");
}

#[test]
fn test_extension_discovery_and_import_paths() {
    let temp_dir = TempDir::new().unwrap();
    let cache = ExtensionSchemaCache::new(temp_dir.path().to_path_buf()).unwrap();

    // Create mock extension structure
    let ext_dir = temp_dir.path().join("hetzner@1.0.0");
    let schemas_dir = ext_dir.join("schemas");
    fs::create_dir_all(&schemas_dir).unwrap();
    fs::write(schemas_dir.join("main.ncl"), "{}").unwrap();

    // List extensions
    let extensions = cache.list_cached_extensions().unwrap();
    assert_eq!(extensions.len(), 1, "Should find hetzner extension");
    assert_eq!(extensions[0].name, "hetzner");
    assert_eq!(extensions[0].version, "1.0.0");

    // Get import paths
    let import_paths = cache.build_import_paths().unwrap();
    assert_eq!(import_paths.len(), 1, "Should have one import path");
    assert!(import_paths[0].exists(), "Import path should exist");

    println!("✓ Extension discovery successful");
}

#[test]
fn test_multiple_extension_versions() {
    let temp_dir = TempDir::new().unwrap();
    let cache = ExtensionSchemaCache::new(temp_dir.path().to_path_buf()).unwrap();

    // Create multiple versions of same extension
    for version in &["1.0.0", "1.1.0", "2.0.0"] {
        let ext_dir = temp_dir.path().join(format!("aws@{}", version));
        let schemas_dir = ext_dir.join("schemas");
        fs::create_dir_all(&schemas_dir).unwrap();
        fs::write(schemas_dir.join("main.ncl"), "{}").unwrap();
    }

    let extensions = cache.list_cached_extensions().unwrap();
    assert_eq!(extensions.len(), 3, "Should find all AWS versions");

    // Test exact version matching
    let path = cache.get_extension_path("aws", Some("1.1.0"));
    assert!(path.is_some(), "Should find exact version");
    assert!(path.unwrap().ends_with("aws@1.1.0/schemas"));

    // Test latest version (first found)
    let path = cache.get_extension_path("aws", None);
    assert!(path.is_some(), "Should find at least one version");

    println!("✓ Multiple extension versions handled correctly");
}

#[test]
fn test_multi_extension_import_paths() {
    let temp_dir = TempDir::new().unwrap();
    let cache = ExtensionSchemaCache::new(temp_dir.path().to_path_buf()).unwrap();

    // Create multiple extensions
    for (name, version) in &[("hetzner", "1.0.0"), ("aws", "2.1.0"), ("upcloud", "1.5.0")] {
        let ext_dir = temp_dir.path().join(format!("{}@{}", name, version));
        let schemas_dir = ext_dir.join("schemas");
        fs::create_dir_all(&schemas_dir).unwrap();
        fs::write(schemas_dir.join("main.ncl"), "{}").unwrap();
    }

    let extensions = cache.list_cached_extensions().unwrap();
    assert_eq!(extensions.len(), 3, "Should find all extensions");

    let import_paths = cache.build_import_paths().unwrap();
    assert_eq!(import_paths.len(), 3, "Should have three import paths");

    // All paths should exist
    for path in &import_paths {
        assert!(path.exists(), "Import path should exist: {:?}", path);
    }

    println!("✓ Multi-extension import paths: {:?}", import_paths);
}

#[test]
fn test_git_repo_structure_in_startup() {
    match load_deployment_mode() {
        Ok(config) => {
            // Just verify that the git config is present in deployment-mode
            let git_config = config.config.get("git");
            assert!(
                git_config.is_some(),
                "Git configuration should be in deployment-mode.ncl"
            );

            let schemas = git_config.unwrap().get("schemas_repo");
            let configs = git_config.unwrap().get("configs_repo");

            assert!(schemas.is_some(), "schemas_repo should be defined");
            assert!(configs.is_some(), "configs_repo should be defined");

            // Verify essential fields
            let schemas = schemas.unwrap();
            assert!(schemas.get("url").is_some(), "URL should be defined");
            assert!(
                schemas.get("cache_dir").is_some(),
                "cache_dir should be defined"
            );

            println!("✓ Git repo structure validated");
        }
        Err(e) => {
            eprintln!("ℹ Deployment mode not found (expected in CI): {}", e);
        }
    }
}

#[test]
fn test_nickel_import_path_priority() {
    // This test verifies that the NICKEL_IMPORT_PATH priority system works
    // (actual priority order is tested via functional tests, not unit tests)

    let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());

    // Priority 1: Extension schemas
    let ext_cache = PathBuf::from(format!("{}/.cache/provisioning/extensions", home));
    if ext_cache.exists() {
        println!("✓ Extension cache dir exists: {:?}", ext_cache);
    }

    // Priority 2: Git schemas
    let schemas_cache = PathBuf::from(format!("{}/.cache/provisioning/schemas", home));
    if schemas_cache.exists() {
        println!("✓ Schemas cache dir exists: {:?}", schemas_cache);
    }

    // Priority 3: Git configs
    let configs_cache = PathBuf::from(format!("{}/.cache/provisioning/configs", home));
    if configs_cache.exists() {
        println!("✓ Configs cache dir exists: {:?}", configs_cache);
    }

    // Priority 4: Local provisioning/schemas
    if PathBuf::from("provisioning/schemas").exists() {
        println!("✓ Local schemas dir exists");
    }

    println!("✓ NICKEL_IMPORT_PATH cache structure verified");
}

#[test]
fn test_service_enabled_checking() {
    match load_deployment_mode() {
        Ok(config) => {
            let startup = PlatformStartup::new(&config.config);
            assert!(startup.is_ok());

            let startup = startup.unwrap();

            // Core services should be checkable
            for service in &["orchestrator", "vault_service"] {
                let enabled = startup.is_service_enabled(service);
                println!(
                    "  • {}: {}",
                    service,
                    if enabled { "enabled" } else { "disabled" }
                );
            }

            println!("✓ Service enablement checking works");
        }
        Err(e) => {
            eprintln!("ℹ Deployment mode not found (expected in CI): {}", e);
        }
    }
}
