//! Comprehensive tests for database module
//!
//! Tests cover:
//! - Configuration creation and defaults
//! - Config conversions from DatabaseConfig and BacktestingDatabaseConfig
//! - Connection pool creation and management
//! - Health checks
//! - Pool statistics and utilization
//! - Error handling

use common::database::{
    DatabaseError, DatabasePool, LocalDatabaseConfig, PerformanceConfig, PoolConfig, PoolStats,
};
use config::database::DatabaseConfig;
use config::database::{PoolConfig as ConfigPoolConfig, TransactionConfig};
use config::structures::BacktestingDatabaseConfig;
use std::time::Duration;

// ============================================================================
// Configuration Tests
// ============================================================================

#[test]
fn test_local_database_config_default() {
    let config = LocalDatabaseConfig::default();

    assert_eq!(
        config.url,
        "postgresql://foxhunt:password@localhost:5432/foxhunt"
    );
    assert_eq!(config.pool.max_connections, 50);
    assert_eq!(config.pool.min_connections, 10);
    assert_eq!(config.performance.query_timeout_micros, 800);
    assert!(config.performance.enable_prewarming);
    assert!(config.performance.enable_prepared_statements);
}

#[test]
fn test_pool_config_default() {
    let config = PoolConfig::default();

    assert_eq!(config.max_connections, 50);
    assert_eq!(config.min_connections, 10);
    assert_eq!(config.connect_timeout_ms, 100);
    assert_eq!(config.acquire_timeout_ms, 50);
    assert_eq!(config.max_lifetime_seconds, 3600);
    assert_eq!(config.idle_timeout_seconds, 300);
}

#[test]
fn test_performance_config_default() {
    let config = PerformanceConfig::default();

    assert_eq!(config.query_timeout_micros, 800);
    assert!(config.enable_prewarming);
    assert!(config.enable_prepared_statements);
    assert!(config.enable_slow_query_logging);
    assert_eq!(config.slow_query_threshold_micros, 1000);
}

#[test]
fn test_from_database_config() {
    let db_config = DatabaseConfig {
        url: "postgresql://test:test@localhost:5432/test".to_string(),
        max_connections: 100,
        min_connections: 20,
        connect_timeout: Duration::from_millis(200),
        query_timeout: Duration::from_micros(1000),
        enable_query_logging: true,
        application_name: Some("test_app".to_string()),
        pool: ConfigPoolConfig::default(),
        transaction: TransactionConfig::default(),
    };

    let local_config: LocalDatabaseConfig = db_config.into();

    assert_eq!(
        local_config.url,
        "postgresql://test:test@localhost:5432/test"
    );
    assert_eq!(local_config.pool.max_connections, 100);
    assert_eq!(local_config.pool.min_connections, 20); // 100 / 5
    assert_eq!(local_config.pool.connect_timeout_ms, 100); // Capped at 100ms for HFT
    assert_eq!(local_config.performance.query_timeout_micros, 800); // Capped at 800μs for HFT
    assert!(local_config.performance.enable_slow_query_logging);
}

#[test]
fn test_from_database_config_min_connections() {
    let db_config = DatabaseConfig {
        url: "postgresql://test:test@localhost:5432/test".to_string(),
        max_connections: 5, // Low value to test minimum
        min_connections: 2,
        connect_timeout: Duration::from_millis(50),
        query_timeout: Duration::from_micros(500),
        enable_query_logging: false,
        application_name: None,
        pool: ConfigPoolConfig::default(),
        transaction: TransactionConfig::default(),
    };

    let local_config: LocalDatabaseConfig = db_config.into();

    assert_eq!(local_config.pool.max_connections, 5);
    assert_eq!(local_config.pool.min_connections, 2); // max(5/5, 2) = 2
}

#[test]
fn test_from_backtesting_database_config() {
    let bt_config = BacktestingDatabaseConfig {
        database_url: "postgresql://bt:bt@localhost:5432/bt".to_string(),
        max_connections: Some(20),
        min_connections: Some(5),
        acquire_timeout_ms: Some(2000),
        statement_cache_capacity: Some(1000),
        enable_logging: Some(true),
    };

    let local_config: LocalDatabaseConfig = bt_config.into();

    assert_eq!(local_config.url, "postgresql://bt:bt@localhost:5432/bt");
    assert_eq!(local_config.pool.max_connections, 20);
    assert_eq!(local_config.pool.min_connections, 5); // 20 / 4
    assert_eq!(local_config.pool.connect_timeout_ms, 2000);
    assert_eq!(local_config.performance.query_timeout_micros, 10000); // 10ms for backtesting
    assert!(local_config.performance.enable_slow_query_logging);
}

#[test]
fn test_from_backtesting_database_config_defaults() {
    let bt_config = BacktestingDatabaseConfig {
        database_url: "postgresql://bt:bt@localhost:5432/bt".to_string(),
        max_connections: None,
        min_connections: None,
        acquire_timeout_ms: None,
        statement_cache_capacity: None,
        enable_logging: None,
    };

    let local_config: LocalDatabaseConfig = bt_config.into();

    assert_eq!(local_config.pool.max_connections, 10); // Default
    assert_eq!(local_config.pool.min_connections, 2); // max(10/4, 2) = 2
    assert_eq!(local_config.pool.connect_timeout_ms, 1000); // Default
    assert!(!local_config.performance.enable_slow_query_logging); // Default false
}

// ============================================================================
// Pool Statistics Tests
// ============================================================================

#[test]
fn test_pool_stats_utilization_percentage() {
    let stats = PoolStats {
        size: 50,
        idle: 30,
        active: 20,
        max_size: 50,
    };

    assert_eq!(stats.utilization_percentage(), 40.0); // 20/50 * 100
}

#[test]
fn test_pool_stats_utilization_full() {
    let stats = PoolStats {
        size: 50,
        idle: 0,
        active: 50,
        max_size: 50,
    };

    assert_eq!(stats.utilization_percentage(), 100.0);
}

#[test]
fn test_pool_stats_utilization_zero() {
    let stats = PoolStats {
        size: 50,
        idle: 50,
        active: 0,
        max_size: 50,
    };

    assert_eq!(stats.utilization_percentage(), 0.0);
}

#[test]
fn test_pool_stats_is_healthy() {
    let stats = PoolStats {
        size: 50,
        idle: 30,
        active: 20,
        max_size: 50,
    };

    assert!(stats.is_healthy()); // 40% < 80%
}

#[test]
fn test_pool_stats_is_unhealthy() {
    let stats = PoolStats {
        size: 50,
        idle: 10,
        active: 40,
        max_size: 50,
    };

    assert!(!stats.is_healthy()); // 80% >= 80%
}

#[test]
fn test_pool_stats_is_unhealthy_critical() {
    let stats = PoolStats {
        size: 50,
        idle: 0,
        active: 50,
        max_size: 50,
    };

    assert!(!stats.is_healthy()); // 100% > 80%
}

// ============================================================================
// Database Error Tests
// ============================================================================

#[test]
fn test_database_error_query_timeout_display() {
    let error = DatabaseError::QueryTimeout {
        actual_ms: 150,
        max_ms: 100,
    };

    let error_str = error.to_string();
    assert!(error_str.contains("Query timeout"));
    assert!(error_str.contains("150ms"));
    assert!(error_str.contains("100ms"));
}

#[test]
fn test_database_error_pool_exhausted_display() {
    let error = DatabaseError::PoolExhausted;

    let error_str = error.to_string();
    assert!(error_str.contains("Pool exhausted"));
    assert!(error_str.contains("no connections available"));
}

#[test]
fn test_database_error_configuration_display() {
    let error = DatabaseError::Configuration("Invalid URL format".to_string());

    let error_str = error.to_string();
    assert!(error_str.contains("Configuration error"));
    assert!(error_str.contains("Invalid URL format"));
}

#[test]
fn test_database_error_performance_display() {
    let error = DatabaseError::Performance("Query exceeded 1ms threshold".to_string());

    let error_str = error.to_string();
    assert!(error_str.contains("Performance violation"));
    assert!(error_str.contains("Query exceeded 1ms threshold"));
}

// ============================================================================
// Integration Tests (require database)
// ============================================================================
// Note: These tests are marked with #[ignore] by default since they require
// a running PostgreSQL instance. Run with: cargo test -- --ignored

#[tokio::test]
#[ignore = "requires PostgreSQL database"]
async fn test_database_pool_creation() {
    let config = LocalDatabaseConfig {
        url: "postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt".to_string(),
        pool: PoolConfig {
            max_connections: 5,
            min_connections: 2,
            connect_timeout_ms: 5000,
            acquire_timeout_ms: 5000,
            max_lifetime_seconds: 3600,
            idle_timeout_seconds: 300,
        },
        performance: PerformanceConfig {
            query_timeout_micros: 800,
            enable_prewarming: false, // Disable prewarming for faster test
            enable_prepared_statements: true,
            enable_slow_query_logging: true,
            slow_query_threshold_micros: 1000,
        },
    };

    let pool = DatabasePool::new(config).await;
    assert!(
        pool.is_ok(),
        "Failed to create database pool: {:?}",
        pool.err()
    );
}

#[tokio::test]
#[ignore = "requires PostgreSQL database"]
async fn test_database_pool_health_check() {
    let config = LocalDatabaseConfig {
        url: "postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt".to_string(),
        ..Default::default()
    };

    let pool = DatabasePool::new(config)
        .await
        .expect("Failed to create pool");
    let health = pool.health_check().await;

    assert!(health.is_ok(), "Health check failed: {:?}", health.err());
}

#[tokio::test]
#[ignore = "requires PostgreSQL database"]
async fn test_database_pool_stats() {
    let config = LocalDatabaseConfig {
        url: "postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt".to_string(),
        pool: PoolConfig {
            max_connections: 10,
            min_connections: 2,
            ..Default::default()
        },
        performance: PerformanceConfig {
            enable_prewarming: false,
            ..Default::default()
        },
    };

    let pool = DatabasePool::new(config)
        .await
        .expect("Failed to create pool");
    let stats = pool.pool_stats();

    assert_eq!(stats.max_size, 10);
    assert!(stats.size <= 10);
    assert!(stats.idle <= stats.size);
    assert_eq!(stats.active, stats.size - stats.idle);
}

#[tokio::test]
#[ignore = "requires PostgreSQL database"]
async fn test_database_pool_invalid_url() {
    let config = LocalDatabaseConfig {
        url: "invalid-url-format".to_string(),
        ..Default::default()
    };

    let result = DatabasePool::new(config).await;
    assert!(result.is_err());

    if let Err(DatabaseError::Configuration(msg)) = result {
        assert!(msg.contains("Invalid URL"));
    } else {
        panic!("Expected Configuration error");
    }
}

#[tokio::test]
#[ignore = "requires PostgreSQL database"]
async fn test_database_pool_connection_failure() {
    let config = LocalDatabaseConfig {
        url: "postgresql://invalid:invalid@localhost:9999/invalid".to_string(),
        pool: PoolConfig {
            connect_timeout_ms: 100, // Fast timeout
            ..Default::default()
        },
        ..Default::default()
    };

    let result = DatabasePool::new(config).await;
    assert!(result.is_err());
    assert!(matches!(result.unwrap_err(), DatabaseError::Connection(_)));
}
