//! Database Test Helper for Foxhunt HFT System
//!
//! Consolidates database connection logic across 22+ test files to eliminate duplication
//! and provide consistent database setup/teardown functionality.
//!
//! This module provides:
//! - get_test_database_pool() - Centralized PostgreSQL pool creation
//! - setup_test_database() - Test database initialization and setup
//! - teardown_test_database() - Test data cleanup and connection closure
//! - Test data creation and management utilities
//! - Consistent database configuration across all tests

use std::collections::HashMap;

use chrono::Utc;
// CANONICAL TYPE IMPORTS - Use core types throughout
// All Decimal operations use rust_decimal::Decimal
use ::rust_decimal::Decimal;
use sqlx::{PgPool, Row};
use std::time::Duration;
use tokio::time::timeout;
use uuid::Uuid;

/// Database test configuration
#[derive(Debug, Clone)]
/// DatabaseTestConfig component.
pub struct DatabaseTestConfig {
    pub postgres_url: String,
    pub influxdb_url: String,
    pub redis_url: String,
    pub test_timeout_secs: u64,
    pub pool_max_size: u32,
    pub pool_timeout_secs: u64,
    pub cleanup_on_drop: bool,
}

impl Default for DatabaseTestConfig {
    fn default() -> Self {
        // Load test environment variables
        crate::load_test_env();

        let db_host = std::env::var("DATABASE_HOST")
            .or_else(|_| std::env::var("POSTGRES_HOST"))
            .unwrap_or_else(|_| "localhost".to_string());

        Self {
            postgres_url: std::env::var("TEST_DATABASE_URL").unwrap_or_else(|_| {
                format!(
                    "postgresql://foxhunt_test:test_password@{}:5433/foxhunt_test",
                    db_host
                )
            }),
            influxdb_url: std::env::var("TEST_INFLUXDB_URL")
                .unwrap_or_else(|_| format!("http://{}:8086", db_host)),
            redis_url: std::env::var("TEST_REDIS_URL")
                .unwrap_or_else(|_| format!("redis://{}:6379/1", db_host)),
            test_timeout_secs: 30,
            pool_max_size: 5,
            pool_timeout_secs: 10,
            cleanup_on_drop: true,
        }
    }
}

impl DatabaseTestConfig {
    /// Create configuration for testing with Docker Compose
    pub fn docker_compose() -> Self {
        let postgres_host = std::env::var("POSTGRES_HOST")
            .or_else(|_| std::env::var("DATABASE_HOST"))
            .unwrap_or_else(|_| "localhost".to_string());

        let influx_host =
            std::env::var("INFLUXDB_HOST").unwrap_or_else(|_| "localhost".to_string());

        let postgres_user =
            std::env::var("POSTGRES_USER").unwrap_or_else(|_| "foxhunt".to_string());
        let postgres_password = std::env::var("POSTGRES_PASSWORD")
            .or_else(|_| std::env::var("TEST_DB_PASSWORD"))
            .unwrap_or_else(|_| "test_password".to_string()); // Default for testing
        let postgres_db =
            std::env::var("POSTGRES_DB").unwrap_or_else(|_| "foxhunt_dev".to_string());

        Self {
            postgres_url: format!(
                "postgresql://{}:{}@{}:5432/{}",
                postgres_user, postgres_password, postgres_host, postgres_db
            ),
            influxdb_url: format!("http://{}:8086", influx_host),
            redis_url: std::env::var("REDIS_URL")
                .unwrap_or_else(|_| format!("redis://{}:6379", postgres_host)),
            ..Default::default()
        }
    }

    /// Create configuration for `CI`/`CD` environments
    pub fn ci_environment() -> Self {
        Self {
            test_timeout_secs: 10, // Shorter timeouts for CI
            pool_max_size: 2,      // Smaller pools for CI
            pool_timeout_secs: 5,
            ..Self::docker_compose()
        }
    }

    /// Validate the configuration
    pub fn validate(&self) -> Result<(), String> {
        if self.postgres_url.is_empty() {
            return Err("PostgreSQL URL cannot be empty".to_string());
        }
        if !self.postgres_url.starts_with("postgresql://") {
            return Err(format!(
                "Invalid PostgreSQL URL format: {}",
                self.postgres_url
            ));
        }
        if self.pool_max_size == 0 {
            return Err("Pool max size must be greater than 0".to_string());
        }
        Ok(())
    }
}

/// Database test pool with cleanup tracking
#[derive(Debug)]
pub struct DatabaseTestPool {
    pub pool: PgPool,
    pub config: DatabaseTestConfig,
    pub test_session_id: Uuid,
    pub created_test_ids: HashMap<String, Vec<Uuid>>,
}

impl DatabaseTestPool {
    /// Create a new test pool with the given configuration
    pub async fn new(config: DatabaseTestConfig) -> Result<Self, sqlx::Error> {
        use tracing::{debug, error};

        let test_session_id = Uuid::new_v4();

        debug!(
            session_id = %test_session_id,
            postgres_url = %config.postgres_url.replace(&std::env::var("POSTGRES_PASSWORD").unwrap_or_default(), "[REDACTED]"),
            pool_max_size = config.pool_max_size,
            pool_timeout_secs = config.pool_timeout_secs,
            "Creating new database test pool"
        );

        config.validate().map_err(|e| {
            error!(
                validation_error = %e,
                session_id = %test_session_id,
                "Database configuration validation failed"
            );
            sqlx::Error::Configuration(e.into())
        })?;

        let pool = timeout(
            Duration::from_secs(config.pool_timeout_secs),
            PgPool::connect(&config.postgres_url)
        )
        .await
        .map_err(|_| {
            error!(
                timeout_secs = config.pool_timeout_secs,
                session_id = %test_session_id,
                "Database connection timed out"
            );
            sqlx::Error::PoolTimedOut
        })?
        .map_err(|e| {
            error!(
                error = %e,
                postgres_url = %config.postgres_url.replace(&std::env::var("POSTGRES_PASSWORD").unwrap_or_default(), "[REDACTED]"),
                session_id = %test_session_id,
                "Failed to connect to PostgreSQL database"
            );
            e
        })?;

        debug!(
            session_id = %test_session_id,
            "Successfully created database test pool"
        );

        Ok(Self {
            pool,
            config,
            test_session_id,
            created_test_ids: HashMap::new(),
        })
    }

    /// Get a reference to the underlying pool
    pub fn pool(&self) -> &PgPool {
        &self.pool
    }

    /// Track created test data for cleanup
    pub fn track_test_data(&mut self, category: &str, id: Uuid) {
        self.created_test_ids
            .entry(category.to_string())
            .or_default()
            .push(id);
    }

    /// Get all tracked test IDs for a category
    pub fn get_tracked_ids(&self, category: &str) -> Vec<Uuid> {
        self.created_test_ids
            .get(category)
            .cloned()
            .unwrap_or_default()
    }

    /// Verify database health
    pub async fn health_check(&self) -> Result<bool, sqlx::Error> {
        use tracing::{debug, error};

        debug!(
            session_id = %self.test_session_id,
            "Starting database health check"
        );

        let row = sqlx::query("SELECT 1 as health_check")
            .fetch_one(&self.pool)
            .await
            .map_err(|e| {
                error!(
                    error = %e,
                    session_id = %self.test_session_id,
                    "Database health check query failed"
                );
                e
            })?;

        let health: i32 = row.get("health_check");
        let is_healthy = health == 1;

        if is_healthy {
            debug!(session_id = %self.test_session_id, "Database health check passed");
        } else {
            error!(
                health_value = health,
                session_id = %self.test_session_id,
                "Database health check returned unexpected value"
            );
        }

        Ok(is_healthy)
    }
}

impl Drop for DatabaseTestPool {
    fn drop(&mut self) {
        if self.config.cleanup_on_drop && !self.created_test_ids.is_empty() {
            tracing::warn!(
                "DatabaseTestPool dropped with {} tracked categories of test data. Consider calling cleanup_all_test_data() explicitly.",
                self.created_test_ids.len()
            );
        }
    }
}

/// Get a configured test database pool - the main entry point for tests
pub async fn get_test_database_pool() -> Result<DatabaseTestPool, sqlx::Error> {
    get_test_database_pool_with_config(DatabaseTestConfig::default()).await
}

/// Get a test database pool with custom configuration
pub async fn get_test_database_pool_with_config(
    config: DatabaseTestConfig,
) -> Result<DatabaseTestPool, sqlx::Error> {
    DatabaseTestPool::new(config).await
}

/// Setup test database with schema validation and initial data
pub async fn setup_test_database(pool: &DatabaseTestPool) -> Result<(), sqlx::Error> {
    // Verify required tables exist
    let required_tables = vec![
        "users",
        "accounts",
        "sessions",
        "orders",
        "executions",
        "positions",
        "trades",
        "risk_limits",
        "risk_events",
        "audit_logs",
    ];

    for table in required_tables {
        let count = sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM information_schema.tables WHERE table_name = $1 AND table_schema = 'public'")
            .bind(table)
            .fetch_one(&pool.pool)
            .await?;

        if count == 0 {
            tracing::warn!(
                "Required table '{}' does not exist - some tests may fail",
                table
            );
        }
    }

    // Verify critical indexes exist
    let critical_indexes = vec![
        "idx_orders_client_order_id_hash",
        "idx_orders_status_partial_active",
        "idx_orders_symbol_time_compound",
        "idx_positions_user_symbol_unique",
        "idx_sessions_token_hash",
    ];

    for index in critical_indexes {
        let exists =
            sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM pg_indexes WHERE indexname = $1")
                .bind(index)
                .fetch_one(&pool.pool)
                .await?;

        if exists == 0 {
            tracing::warn!(
                "Critical index '{}' does not exist - performance may be affected",
                index
            );
        }
    }

    tracing::info!(
        "✅ Test database setup completed for session {}",
        pool.test_session_id
    );
    Ok(())
}

/// Teardown test database by cleaning up test data and closing connections
pub async fn teardown_test_database(mut pool: DatabaseTestPool) -> Result<(), sqlx::Error> {
    // Clean up all tracked test data
    cleanup_all_test_data(&mut pool).await?;

    // Close the pool
    pool.pool.close().await;

    tracing::info!(
        "✅ Test database teardown completed for session {}",
        pool.test_session_id
    );
    Ok(())
}

/// Create test user data with proper tracking
pub async fn create_test_user(
    pool: &mut DatabaseTestPool,
    username_suffix: Option<&str>,
) -> Result<(Uuid, Uuid), sqlx::Error> {
    use tracing::{debug, error};

    let user_id = Uuid::new_v4();
    let username = format!(
        "test_user_{}_{}",
        username_suffix.unwrap_or("default"),
        user_id.to_string().chars().take(8).collect::<String>()
    );
    let email = format!("test_{}@foxhunt.trading", user_id);

    debug!(
        user_id = %user_id,
        username = %username,
        email = %email,
        session_id = %pool.test_session_id,
        "Creating test user and account"
    );

    // Create user
    sqlx::query("INSERT INTO users (user_id, username, email, password_hash, created_at) VALUES ($1, $2, $3, $4, $5)")
        .bind(user_id)
        .bind(username.clone())
        .bind(email.clone())
        .bind("test_hash")
        .bind(Utc::now())
    .execute(&pool.pool)
    .await
    .map_err(|e| {
        error!(
            error = %e,
            user_id = %user_id,
            username = %username,
            email = %email,
            session_id = %pool.test_session_id,
            "Failed to create test user in database"
        );
        e
    })?;

    pool.track_test_data("users", user_id);

    // Create test account
    let account_id = Uuid::new_v4();
    let balance = Decimal::new(100000, 2); // $1000.00

    sqlx::query("INSERT INTO accounts (account_id, user_id, account_type, balance, created_at) VALUES ($1, $2, $3, $4, $5)")
        .bind(account_id)
        .bind(user_id)
        .bind("TRADING")
        .bind(balance)
        .bind(Utc::now())
    .execute(&pool.pool)
    .await
    .map_err(|e| {
        error!(
            error = %e,
            account_id = %account_id,
            user_id = %user_id,
            balance = %balance,
            session_id = %pool.test_session_id,
            "Failed to create test account in database"
        );
        e
    })?;

    pool.track_test_data("accounts", account_id);

    debug!(
        user_id = %user_id,
        account_id = %account_id,
        session_id = %pool.test_session_id,
        "Successfully created test user and account"
    );

    Ok((user_id, account_id))
}

/// Create test order data
pub async fn create_test_order(
    pool: &mut DatabaseTestPool,
    user_id: Uuid,
    account_id: Uuid,
    symbol: &str,
    side: &str,
    quantity: i64,
    price: Decimal,
) -> Result<Uuid, sqlx::Error> {
    let order_id = Uuid::new_v4();
    let client_order_id = format!("TEST_ORDER_{}", Uuid::new_v4());

    sqlx::query("INSERT INTO orders (order_id, user_id, account_id, client_order_id, symbol, order_type, side, quantity, price, status, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)")
        .bind(order_id)
        .bind(user_id)
        .bind(account_id)
        .bind(client_order_id)
        .bind(symbol)
        .bind("LIMIT")
        .bind(side)
        .bind(quantity)
        .bind(price)
        .bind("PENDING")
        .bind(Utc::now())
    .execute(&pool.pool)
    .await?;

    pool.track_test_data("orders", order_id);
    Ok(order_id)
}

/// Create test position data
pub async fn create_test_position(
    pool: &mut DatabaseTestPool,
    user_id: Uuid,
    symbol: &str,
    quantity: i64,
    average_price: Decimal,
) -> Result<Uuid, sqlx::Error> {
    let position_id = Uuid::new_v4();
    let market_value = average_price * Decimal::from(quantity.abs());

    sqlx::query("INSERT INTO positions (position_id, user_id, symbol, quantity, average_price, market_value, unrealized_pnl, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)")
        .bind(position_id)
        .bind(user_id)
        .bind(symbol)
        .bind(quantity)
        .bind(average_price)
        .bind(market_value)
        .bind(Decimal::new(0, 2))
        .bind(Utc::now())
        .bind(Utc::now())
    .execute(&pool.pool)
    .await?;

    pool.track_test_data("positions", position_id);
    Ok(position_id)
}

/// Create test execution data
pub async fn create_test_execution(
    pool: &mut DatabaseTestPool,
    order_id: Uuid,
    user_id: Uuid,
    symbol: &str,
    side: &str,
    quantity: i64,
    price: Decimal,
) -> Result<Uuid, sqlx::Error> {
    let execution_id = Uuid::new_v4();

    sqlx::query("INSERT INTO executions (execution_id, order_id, user_id, symbol, side, quantity, price, executed_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)")
        .bind(execution_id)
        .bind(order_id)
        .bind(user_id)
        .bind(symbol)
        .bind(side)
        .bind(quantity)
        .bind(price)
        .bind(Utc::now())
    .execute(&pool.pool)
    .await?;

    pool.track_test_data("executions", execution_id);
    Ok(execution_id)
}

/// Cleanup all tracked test data in proper dependency order
pub async fn cleanup_all_test_data(pool: &mut DatabaseTestPool) -> Result<(), sqlx::Error> {
    use tracing::{debug, error};

    let session_id = pool.test_session_id;
    let total_categories = pool.created_test_ids.len();

    debug!(
        session_id = %session_id,
        categories = total_categories,
        "Starting cleanup of all tracked test data"
    );

    // Delete in reverse dependency order to avoid foreign key violations

    // 1. Clean up executions
    let execution_ids = pool.get_tracked_ids("executions");
    if !execution_ids.is_empty() {
        debug!(count = execution_ids.len(), "Cleaning up executions");
        for execution_id in execution_ids {
            sqlx::query("DELETE FROM executions WHERE execution_id = $1")
                .bind(execution_id)
                .execute(&pool.pool)
                .await
                .map_err(|e| {
                    error!(
                        error = %e,
                        execution_id = %execution_id,
                        session_id = %session_id,
                        "Failed to delete execution during cleanup"
                    );
                    e
                })?;
        }
    }

    // 2. Clean up orders
    let order_ids = pool.get_tracked_ids("orders");
    if !order_ids.is_empty() {
        debug!(count = order_ids.len(), "Cleaning up orders");
        for order_id in order_ids {
            sqlx::query("DELETE FROM orders WHERE order_id = $1")
                .bind(order_id)
                .execute(&pool.pool)
                .await
                .map_err(|e| {
                    error!(
                        error = %e,
                        order_id = %order_id,
                        session_id = %session_id,
                        "Failed to delete order during cleanup"
                    );
                    e
                })?;
        }
    }

    // 3. Clean up positions
    let position_ids = pool.get_tracked_ids("positions");
    if !position_ids.is_empty() {
        debug!(count = position_ids.len(), "Cleaning up positions");
        for position_id in position_ids {
            sqlx::query("DELETE FROM positions WHERE position_id = $1")
                .bind(position_id)
                .execute(&pool.pool)
                .await
                .map_err(|e| {
                    error!(
                        error = %e,
                        position_id = %position_id,
                        session_id = %session_id,
                        "Failed to delete position during cleanup"
                    );
                    e
                })?;
        }
    }

    // 4. Clean up accounts
    let account_ids = pool.get_tracked_ids("accounts");
    if !account_ids.is_empty() {
        debug!(count = account_ids.len(), "Cleaning up accounts");
        for account_id in account_ids {
            sqlx::query("DELETE FROM accounts WHERE account_id = $1")
                .bind(account_id)
                .execute(&pool.pool)
                .await
                .map_err(|e| {
                    error!(
                        error = %e,
                        account_id = %account_id,
                        session_id = %session_id,
                        "Failed to delete account during cleanup"
                    );
                    e
                })?;
        }
    }

    // 5. Clean up sessions
    let session_ids = pool.get_tracked_ids("sessions");
    if !session_ids.is_empty() {
        debug!(count = session_ids.len(), "Cleaning up sessions");
        for tracked_session_id in session_ids {
            sqlx::query("DELETE FROM sessions WHERE session_id = $1")
                .bind(tracked_session_id)
                .execute(&pool.pool)
                .await
                .map_err(|e| {
                    error!(
                        error = %e,
                        tracked_session_id = %tracked_session_id,
                        session_id = %session_id,
                        "Failed to delete session during cleanup"
                    );
                    e
                })?;
        }
    }

    // 6. Clean up users (last due to foreign key dependencies)
    let user_ids = pool.get_tracked_ids("users");
    if !user_ids.is_empty() {
        debug!(count = user_ids.len(), "Cleaning up users");
        for user_id in user_ids {
            sqlx::query("DELETE FROM users WHERE user_id = $1")
                .bind(user_id)
                .execute(&pool.pool)
                .await
                .map_err(|e| {
                    error!(
                        error = %e,
                        user_id = %user_id,
                        session_id = %session_id,
                        "Failed to delete user during cleanup"
                    );
                    e
                })?;
        }
    }

    pool.created_test_ids.clear();

    tracing::info!(
        session_id = %session_id,
        "✅ Successfully cleaned up all test data"
    );
    Ok(())
}

/// Cleanup specific category of test data
pub async fn cleanup_test_data_category(
    pool: &mut DatabaseTestPool,
    category: &str,
) -> Result<(), sqlx::Error> {
    let ids = pool.get_tracked_ids(category);
    if ids.is_empty() {
        return Ok(());
    }

    match category {
        "users" => {
            for user_id in ids {
                // Delete all related data first
                sqlx::query("DELETE FROM executions WHERE user_id = $1")
                    .bind(user_id)
                    .execute(&pool.pool)
                    .await?;
                sqlx::query("DELETE FROM orders WHERE user_id = $1")
                    .bind(user_id)
                    .execute(&pool.pool)
                    .await?;
                sqlx::query("DELETE FROM positions WHERE user_id = $1")
                    .bind(user_id)
                    .execute(&pool.pool)
                    .await?;
                sqlx::query("DELETE FROM accounts WHERE user_id = $1")
                    .bind(user_id)
                    .execute(&pool.pool)
                    .await?;
                sqlx::query("DELETE FROM sessions WHERE user_id = $1")
                    .bind(user_id)
                    .execute(&pool.pool)
                    .await?;
                sqlx::query("DELETE FROM users WHERE user_id = $1")
                    .bind(user_id)
                    .execute(&pool.pool)
                    .await?;
            }
        },
        "orders" => {
            for order_id in ids {
                sqlx::query("DELETE FROM executions WHERE order_id = $1")
                    .bind(order_id)
                    .execute(&pool.pool)
                    .await?;
                sqlx::query("DELETE FROM orders WHERE order_id = $1")
                    .bind(order_id)
                    .execute(&pool.pool)
                    .await?;
            }
        },
        "accounts" => {
            for account_id in ids {
                sqlx::query("DELETE FROM orders WHERE account_id = $1")
                    .bind(account_id)
                    .execute(&pool.pool)
                    .await?;
                sqlx::query("DELETE FROM accounts WHERE account_id = $1")
                    .bind(account_id)
                    .execute(&pool.pool)
                    .await?;
            }
        },
        "positions" => {
            for position_id in ids {
                sqlx::query("DELETE FROM positions WHERE position_id = $1")
                    .bind(position_id)
                    .execute(&pool.pool)
                    .await?;
            }
        },
        "executions" => {
            for execution_id in ids {
                sqlx::query("DELETE FROM executions WHERE execution_id = $1")
                    .bind(execution_id)
                    .execute(&pool.pool)
                    .await?;
            }
        },
        "sessions" => {
            for session_id in ids {
                sqlx::query("DELETE FROM sessions WHERE session_id = $1")
                    .bind(session_id)
                    .execute(&pool.pool)
                    .await?;
            }
        },
        _ => {
            tracing::warn!("Unknown test data category: {}", category);
        },
    }

    pool.created_test_ids.remove(category);
    Ok(())
}

/// Test database performance with a set number of operations
pub async fn benchmark_database_operations(
    pool: &DatabaseTestPool,
    operation_count: usize,
) -> Result<DatabaseBenchmarkResult, sqlx::Error> {
    use std::time::Instant;

    let start_time = Instant::now();
    let mut successful_ops = 0;
    let mut failed_ops = 0;
    let mut min_latency = u64::MAX;
    let mut max_latency = 0u64;
    let mut total_latency = 0u64;

    for i in 0..operation_count {
        let op_start = Instant::now();

        // Perform a simple query
        match sqlx::query("SELECT $1 as test_value")
            .bind(i as i32)
            .fetch_one(&pool.pool)
            .await
        {
            Ok(_) => {
                successful_ops += 1;
                let latency = op_start.elapsed().as_micros() as u64;
                total_latency += latency;
                min_latency = min_latency.min(latency);
                max_latency = max_latency.max(latency);
            },
            Err(_) => failed_ops += 1,
        }
    }

    let total_duration = start_time.elapsed();

    Ok(DatabaseBenchmarkResult {
        total_operations: operation_count,
        successful_operations: successful_ops,
        failed_operations: failed_ops,
        total_duration_ms: total_duration.as_millis() as u64,
        average_latency_us: if successful_ops > 0 {
            total_latency / successful_ops as u64
        } else {
            0
        },
        min_latency_us: if successful_ops > 0 { min_latency } else { 0 },
        max_latency_us: max_latency,
        operations_per_second: (successful_ops as f64 / total_duration.as_secs_f64()) as u64,
    })
}

#[derive(Debug)]
/// DatabaseBenchmarkResult component.
pub struct DatabaseBenchmarkResult {
    pub total_operations: usize,
    pub successful_operations: usize,
    pub failed_operations: usize,
    pub total_duration_ms: u64,
    pub average_latency_us: u64,
    pub min_latency_us: u64,
    pub max_latency_us: u64,
    pub operations_per_second: u64,
}

impl DatabaseBenchmarkResult {
    /// Check if performance meets `HFT` requirements
    pub fn meets_hft_requirements(&self) -> bool {
        self.average_latency_us < 1000 && // < 1ms average
        self.max_latency_us < 10000 &&    // < 10ms max
        self.operations_per_second > 1000 // > 1000 ops/sec
    }
}

/// Convenience macro for setting up a test database with cleanup
#[macro_export]
macro_rules! with_test_database {
    ($pool_var:ident, $test_body:block) => {{
        use $crate::common::database_helper::{get_test_database_pool, setup_test_database, teardown_test_database};

        let mut $pool_var = get_test_database_pool().await
            .expect("Failed to get test database pool");

        setup_test_database(&$pool_var).await
            .expect("Failed to setup test database");

        let result = async move $test_body.await;

        teardown_test_database($pool_var).await
            .expect("Failed to teardown test database");

        result
    }};
}

/// Convenience macro for creating test data with automatic cleanup tracking
#[macro_export]
macro_rules! create_test_data {
    ($pool:expr_2021, user) => {{
        use $crate::common::database_helper::create_test_user;
        create_test_user($pool, None).await
    }};

    ($pool:expr_2021, user, $suffix:expr_2021) => {{
        use $crate::common::database_helper::create_test_user;
        create_test_user($pool, Some($suffix)).await
    }};

    ($pool:expr_2021, order, $user_id:expr_2021, $account_id:expr_2021, $symbol:expr_2021, $side:expr_2021, $quantity:expr_2021, $price:expr_2021) => {{
        use $crate::common::database_helper::create_test_order;
        create_test_order(
            $pool,
            $user_id,
            $account_id,
            $symbol,
            $side,
            $quantity,
            $price,
        )
        .await
    }};
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_database_config_validation() {
        let valid_config = DatabaseTestConfig::default();
        assert!(valid_config.validate().is_ok());

        let mut invalid_config = DatabaseTestConfig::default();
        invalid_config.postgres_url = String::new();
        assert!(invalid_config.validate().is_err());
    }

    #[test]
    fn test_docker_compose_config() {
        std::env::set_var("POSTGRES_HOST", "db-postgres");
        std::env::set_var("INFLUXDB_HOST", "db-influx");

        let config = DatabaseTestConfig::docker_compose();
        assert!(config.postgres_url.contains("db-postgres"));
        assert!(config.influxdb_url.contains("db-influx"));

        std::env::remove_var("POSTGRES_HOST");
        std::env::remove_var("INFLUXDB_HOST");
    }

    #[tokio::test]
    async fn test_database_helper_functionality() {
        // Test configuration creation and validation
        let config = DatabaseTestConfig::default();
        assert!(config.validate().is_ok());

        // This test validates the helper functions work correctly
        // without requiring an actual database connection
        println!("✅ Database test helper functionality validated");
    }
}
