//! Factory pattern and configuration validation tests
//!
//! This module tests the storage factory pattern, configuration validation,
//! CLI argument parsing, and backend selection with feature flag handling.
//!
//! NOTE: These tests are disabled due to import issues with crate name
//! (orchestrator crate name vs provisioning-orchestrator)

// Disabled: import issues with crate name
#![allow(unexpected_cfgs)]
#![cfg(feature = "factory_tests")]
#![allow(unused_imports, clippy::single_component_path_imports)]

use std::path::PathBuf;

use tempfile::TempDir;
use tokio_test;

// Import test helpers and orchestrator types
mod helpers;
use helpers::{StorageAssertions, TestEnvironment};
use orchestrator::storage::{
    available_storage_types, create_storage, create_storage_from_args, storage_help_text,
    DefaultStorageFactory, StorageConfig, StorageFactory, TaskStorage,
};
use orchestrator::{Args, TaskStatus};

/// Test storage configuration creation and validation
#[tokio::test]
async fn test_storage_config_creation() {
    let temp_dir = TempDir::new().unwrap();
    let data_path = temp_dir.path().to_string_lossy().to_string();

    // Test filesystem configuration
    let fs_config = StorageConfig {
        storage_type: "filesystem".to_string(),
        data_dir: data_path.clone(),
        ..Default::default()
    };

    assert!(fs_config.validate().is_ok());

    #[cfg(feature = "surrealdb")]
    {
        // Test SurrealDB embedded configuration
        let embedded_config = StorageConfig {
            storage_type: "surrealdb-embedded".to_string(),
            data_dir: data_path.clone(),
            surrealdb_namespace: Some("test_ns".to_string()),
            surrealdb_database: Some("test_db".to_string()),
            ..Default::default()
        };

        assert!(embedded_config.validate().is_ok());

        // Test SurrealDB server configuration
        let server_config = StorageConfig {
            storage_type: "surrealdb-server".to_string(),
            data_dir: "".to_string(),
            surrealdb_url: Some("ws://localhost:8000".to_string()),
            surrealdb_namespace: Some("test_ns".to_string()),
            surrealdb_database: Some("test_db".to_string()),
            surrealdb_username: Some("admin".to_string()),
            surrealdb_password: Some("password".to_string()),
        };

        assert!(server_config.validate().is_ok());
    }
}

/// Test storage configuration validation failures
#[tokio::test]
async fn test_storage_config_validation_failures() {
    // Test filesystem config with empty data_dir
    let invalid_fs_config = StorageConfig {
        storage_type: "filesystem".to_string(),
        data_dir: "".to_string(),
        ..Default::default()
    };
    assert!(invalid_fs_config.validate().is_err());

    #[cfg(feature = "surrealdb")]
    {
        // Test embedded config with empty data_dir
        let invalid_embedded_config = StorageConfig {
            storage_type: "surrealdb-embedded".to_string(),
            data_dir: "".to_string(),
            ..Default::default()
        };
        assert!(invalid_embedded_config.validate().is_err());

        // Test server config with missing URL
        let invalid_server_config = StorageConfig {
            storage_type: "surrealdb-server".to_string(),
            surrealdb_username: Some("admin".to_string()),
            surrealdb_password: Some("password".to_string()),
            ..Default::default()
        };
        assert!(invalid_server_config.validate().is_err());

        // Test server config with missing credentials
        let incomplete_server_config = StorageConfig {
            storage_type: "surrealdb-server".to_string(),
            surrealdb_url: Some("ws://localhost:8000".to_string()),
            ..Default::default()
        };
        assert!(incomplete_server_config.validate().is_err());
    }

    // Test unknown storage type
    let unknown_config = StorageConfig {
        storage_type: "unknown_backend".to_string(),
        data_dir: "/tmp".to_string(),
        ..Default::default()
    };
    assert!(unknown_config.validate().is_err());

    #[cfg(not(feature = "surrealdb"))]
    {
        // Test SurrealDB config without feature
        let surrealdb_without_feature = StorageConfig {
            storage_type: "surrealdb-embedded".to_string(),
            data_dir: "/tmp".to_string(),
            ..Default::default()
        };
        let validation_result = surrealdb_without_feature.validate();
        assert!(validation_result.is_err());
        assert!(validation_result
            .unwrap_err()
            .to_string()
            .contains("surrealdb' feature"));
    }
}

/// Test CLI args to storage config conversion
#[tokio::test]
async fn test_args_to_storage_config_conversion() {
    let temp_dir = TempDir::new().unwrap();
    let data_path = temp_dir.path().to_string_lossy().to_string();

    // Test filesystem args
    let fs_args = Args {
        port: 8080,
        data_dir: data_path.clone(),
        storage_type: "filesystem".to_string(),
        surrealdb_url: None,
        surrealdb_namespace: Some("default".to_string()),
        surrealdb_database: Some("tasks".to_string()),
        surrealdb_username: None,
        surrealdb_password: None,
        nu_path: "nu".to_string(),
        provisioning_path: "./core/nulib/provisioning".to_string(),
    };

    let config = StorageConfig::from_args(&fs_args);
    assert_eq!(config.storage_type, "filesystem");
    assert_eq!(config.data_dir, data_path);
    assert!(config.validate().is_ok());

    #[cfg(feature = "surrealdb")]
    {
        // Test SurrealDB server args
        let server_args = Args {
            storage_type: "surrealdb-server".to_string(),
            surrealdb_url: Some("ws://localhost:8000".to_string()),
            surrealdb_username: Some("admin".to_string()),
            surrealdb_password: Some("secret".to_string()),
            ..fs_args.clone()
        };

        let server_config = StorageConfig::from_args(&server_args);
        assert_eq!(server_config.storage_type, "surrealdb-server");
        assert_eq!(
            server_config.surrealdb_url,
            Some("ws://localhost:8000".to_string())
        );
        assert_eq!(server_config.surrealdb_username, Some("admin".to_string()));
        assert!(server_config.validate().is_ok());
    }
}

/// Test factory pattern storage creation
#[tokio::test]
async fn test_factory_storage_creation() {
    let temp_dir = TempDir::new().unwrap();
    let data_path = temp_dir.path().to_string_lossy().to_string();

    // Test filesystem storage creation
    let fs_config = StorageConfig {
        storage_type: "filesystem".to_string(),
        data_dir: data_path.clone(),
        ..Default::default()
    };

    let storage = DefaultStorageFactory::create_storage(fs_config)
        .await
        .unwrap();
    StorageAssertions::assert_healthy(&storage).await.unwrap();

    // Verify storage works
    let task = helpers::TestDataGenerator::new().workflow_task();
    storage.enqueue(task.clone(), 1).await.unwrap();
    StorageAssertions::assert_task_exists(&storage, &task.id, &task.name)
        .await
        .unwrap();

    #[cfg(feature = "surrealdb")]
    {
        // Test SurrealDB embedded storage creation
        let embedded_temp_dir = TempDir::new().unwrap();
        let embedded_path = embedded_temp_dir.path().to_string_lossy().to_string();

        let embedded_config = StorageConfig {
            storage_type: "surrealdb-embedded".to_string(),
            data_dir: embedded_path,
            surrealdb_namespace: Some("test".to_string()),
            surrealdb_database: Some("test".to_string()),
            ..Default::default()
        };

        let embedded_storage = DefaultStorageFactory::create_storage(embedded_config)
            .await
            .unwrap();
        StorageAssertions::assert_healthy(&embedded_storage)
            .await
            .unwrap();

        // Test SurrealDB server storage creation (using memory for testing)
        let server_config = StorageConfig {
            storage_type: "surrealdb-server".to_string(),
            data_dir: "".to_string(),
            surrealdb_url: Some("memory://test".to_string()),
            surrealdb_namespace: Some("test".to_string()),
            surrealdb_database: Some("test".to_string()),
            surrealdb_username: Some("test".to_string()),
            surrealdb_password: Some("test".to_string()),
        };

        let server_storage = DefaultStorageFactory::create_storage(server_config)
            .await
            .unwrap();
        StorageAssertions::assert_healthy(&server_storage)
            .await
            .unwrap();
    }
}

/// Test factory creation from CLI args
#[tokio::test]
async fn test_factory_from_cli_args() {
    let temp_dir = TempDir::new().unwrap();
    let data_path = temp_dir.path().to_string_lossy().to_string();

    let args = Args {
        port: 8080,
        data_dir: data_path,
        storage_type: "filesystem".to_string(),
        surrealdb_url: None,
        surrealdb_namespace: Some("orchestrator".to_string()),
        surrealdb_database: Some("tasks".to_string()),
        surrealdb_username: None,
        surrealdb_password: None,
        nu_path: "nu".to_string(),
        provisioning_path: "./core/nulib/provisioning".to_string(),
    };

    let storage = create_storage_from_args(&args).await.unwrap();
    StorageAssertions::assert_healthy(&storage).await.unwrap();

    // Test basic functionality
    let gen = helpers::TestDataGenerator::new();
    let task = gen.workflow_task();
    storage.enqueue(task.clone(), 1).await.unwrap();
    StorageAssertions::assert_task_count(&storage, 1)
        .await
        .unwrap();
}

/// Test factory error handling
#[tokio::test]
async fn test_factory_error_handling() {
    // Test invalid storage type
    let invalid_config = StorageConfig {
        storage_type: "invalid_backend".to_string(),
        data_dir: "/tmp".to_string(),
        ..Default::default()
    };

    let result = DefaultStorageFactory::create_storage(invalid_config).await;
    assert!(result.is_err());
    assert!(result
        .unwrap_err()
        .to_string()
        .contains("Unsupported storage type"));

    // Test invalid data directory
    let invalid_dir_config = StorageConfig {
        storage_type: "filesystem".to_string(),
        data_dir: "/invalid/nonexistent/path".to_string(),
        ..Default::default()
    };

    let result = DefaultStorageFactory::create_storage(invalid_dir_config).await;
    assert!(result.is_err());

    #[cfg(feature = "surrealdb")]
    {
        // Test invalid SurrealDB server URL
        let invalid_server_config = StorageConfig {
            storage_type: "surrealdb-server".to_string(),
            surrealdb_url: Some("invalid://url".to_string()),
            surrealdb_username: Some("test".to_string()),
            surrealdb_password: Some("test".to_string()),
            ..Default::default()
        };

        let result = DefaultStorageFactory::create_storage(invalid_server_config).await;
        // This may or may not fail depending on SurrealDB's URL validation
        // We just ensure it doesn't panic
    }

    #[cfg(not(feature = "surrealdb"))]
    {
        // Test SurrealDB config without feature enabled
        let surrealdb_config = StorageConfig {
            storage_type: "surrealdb-embedded".to_string(),
            data_dir: "/tmp".to_string(),
            ..Default::default()
        };

        let result = DefaultStorageFactory::create_storage(surrealdb_config).await;
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("surrealdb' feature"));
    }
}

/// Test available storage types function
#[test]
fn test_available_storage_types() {
    let types = available_storage_types();

    // Filesystem should always be available
    assert!(types.contains(&"filesystem".to_string()));

    #[cfg(feature = "surrealdb")]
    {
        assert!(types.contains(&"surrealdb-embedded".to_string()));
        assert!(types.contains(&"surrealdb-server".to_string()));
        assert_eq!(types.len(), 3);
    }

    #[cfg(not(feature = "surrealdb"))]
    {
        assert_eq!(types.len(), 1);
    }

    // Verify no duplicates
    let mut sorted_types = types.clone();
    sorted_types.sort();
    sorted_types.dedup();
    assert_eq!(types.len(), sorted_types.len());
}

/// Test storage help text generation
#[test]
fn test_storage_help_text() {
    let help_text = storage_help_text();

    // Should contain information about filesystem
    assert!(help_text.contains("filesystem"));
    assert!(help_text.contains("JSON files"));
    assert!(help_text.contains("--data-dir"));

    #[cfg(feature = "surrealdb")]
    {
        assert!(help_text.contains("surrealdb-embedded"));
        assert!(help_text.contains("surrealdb-server"));
        assert!(help_text.contains("--surrealdb-url"));
        assert!(help_text.contains("--surrealdb-username"));
    }

    #[cfg(not(feature = "surrealdb"))]
    {
        assert!(help_text.contains("SurrealDB backends require compilation"));
    }

    // Should be well-formatted
    assert!(help_text.contains("Available storage backends"));
    assert!(help_text.contains("Example:"));
}

/// Test configuration edge cases
#[tokio::test]
async fn test_configuration_edge_cases() {
    // Test default configuration
    let default_config = StorageConfig::default();
    assert_eq!(default_config.storage_type, "filesystem");
    assert_eq!(default_config.data_dir, "./data");
    assert_eq!(
        default_config.surrealdb_namespace,
        Some("orchestrator".to_string())
    );
    assert_eq!(default_config.surrealdb_database, Some("tasks".to_string()));

    // Test configuration with empty optional fields
    let minimal_config = StorageConfig {
        storage_type: "filesystem".to_string(),
        data_dir: "/tmp".to_string(),
        surrealdb_url: None,
        surrealdb_namespace: None,
        surrealdb_database: None,
        surrealdb_username: None,
        surrealdb_password: None,
    };
    assert!(minimal_config.validate().is_ok());

    #[cfg(feature = "surrealdb")]
    {
        // Test SurrealDB with default namespace/database
        let embedded_minimal = StorageConfig {
            storage_type: "surrealdb-embedded".to_string(),
            data_dir: "/tmp".to_string(),
            surrealdb_namespace: None,
            surrealdb_database: None,
            ..Default::default()
        };
        assert!(embedded_minimal.validate().is_ok());
    }
}

/// Test concurrent factory usage
#[tokio::test]
async fn test_concurrent_factory_usage() {
    use tokio::task::JoinSet;

    let mut join_set = JoinSet::new();
    let mut temp_dirs = Vec::new();

    // Create multiple storage instances concurrently
    for i in 0..5 {
        let temp_dir = TempDir::new().unwrap();
        let data_path = temp_dir.path().to_string_lossy().to_string();

        let config = StorageConfig {
            storage_type: "filesystem".to_string(),
            data_dir: data_path,
            ..Default::default()
        };

        temp_dirs.push(temp_dir); // Keep alive

        join_set.spawn(async move {
            let storage = DefaultStorageFactory::create_storage(config).await?;
            storage.health_check().await?;

            // Test basic operation
            let gen = helpers::TestDataGenerator::new();
            let task = gen.workflow_task();
            storage.enqueue(task, 1).await?;
            storage.total_tasks().await
        });
    }

    // Wait for all factories to complete
    let mut success_count = 0;
    while let Some(result) = join_set.join_next().await {
        match result.unwrap() {
            Ok(task_count) => {
                assert_eq!(task_count, 1);
                success_count += 1;
            }
            Err(e) => panic!("Factory creation failed: {}", e),
        }
    }

    assert_eq!(success_count, 5);
}

/// Test storage type validation in CLI argument parsing
#[test]
fn test_storage_type_validation() {
    use orchestrator::validate_storage_type;

    // Valid storage types
    assert!(validate_storage_type("filesystem").is_ok());

    #[cfg(feature = "surrealdb")]
    {
        assert!(validate_storage_type("surrealdb-embedded").is_ok());
        assert!(validate_storage_type("surrealdb-server").is_ok());
    }

    // Invalid storage type
    let result = validate_storage_type("invalid_type");
    assert!(result.is_err());
    assert!(result.unwrap_err().contains("Invalid storage type"));
    assert!(result.unwrap_err().contains("Available types:"));
}

/// Test factory with custom storage implementations
#[tokio::test]
async fn test_factory_extensibility() {
    // This test verifies that the factory pattern can be extended
    // The current implementation is focused on the built-in types,
    // but the trait-based design allows for future extensions

    let available_types = available_storage_types();
    println!("Available storage types: {:?}", available_types);

    // Verify the factory returns appropriate errors for unknown types
    let unknown_config = StorageConfig {
        storage_type: "custom_future_backend".to_string(),
        data_dir: "/tmp".to_string(),
        ..Default::default()
    };

    let result = DefaultStorageFactory::create_storage(unknown_config).await;
    assert!(result.is_err());

    let error_msg = result.unwrap_err().to_string();
    assert!(error_msg.contains("Unsupported storage type"));
    assert!(error_msg.contains("custom_future_backend"));
}

/// Test factory initialization order and dependencies
#[tokio::test]
async fn test_factory_initialization_order() {
    let temp_dir = TempDir::new().unwrap();
    let data_path = temp_dir.path().to_string_lossy().to_string();

    let config = StorageConfig {
        storage_type: "filesystem".to_string(),
        data_dir: data_path,
        ..Default::default()
    };

    // Factory should handle initialization internally
    let storage = DefaultStorageFactory::create_storage(config).await.unwrap();

    // Storage should be ready for use immediately
    StorageAssertions::assert_healthy(&storage).await.unwrap();

    // Should be able to perform operations without additional setup
    let gen = helpers::TestDataGenerator::new();
    let task = gen.workflow_task();
    storage.enqueue(task.clone(), 1).await.unwrap();

    let retrieved = storage.get_task(&task.id).await.unwrap();
    assert!(retrieved.is_some());
    assert_eq!(retrieved.unwrap().id, task.id);
}

/// Test factory with different configuration formats
#[tokio::test]
async fn test_factory_configuration_formats() {
    let temp_dir = TempDir::new().unwrap();
    let data_path = temp_dir.path();

    // Test with PathBuf
    let path_config = StorageConfig {
        storage_type: "filesystem".to_string(),
        data_dir: data_path.to_string_lossy().to_string(),
        ..Default::default()
    };

    let storage = DefaultStorageFactory::create_storage(path_config)
        .await
        .unwrap();
    StorageAssertions::assert_healthy(&storage).await.unwrap();

    // Test with relative path
    let relative_config = StorageConfig {
        storage_type: "filesystem".to_string(),
        data_dir: "./test_data".to_string(),
        ..Default::default()
    };

    let storage = DefaultStorageFactory::create_storage(relative_config)
        .await
        .unwrap();
    StorageAssertions::assert_healthy(&storage).await.unwrap();

    // Cleanup
    if std::path::Path::new("./test_data").exists() {
        std::fs::remove_dir_all("./test_data").ok();
    }
}
