//! Comprehensive Test Fixtures System for Foxhunt HFT Trading System
//!
//! This module provides a production-ready test infrastructure with:
//! - Standardized test symbols for all asset classes
//! - Configurable test data generators
//! - Reusable test scenarios and utilities
//! - Mock services and database helpers
//!
//! ## Usage
//!
//! ```rust
//! use tests::fixtures::{TEST_EQUITY_1, TEST_FOREX_1, generate_test_symbol};
//! use tests::fixtures::builders::PortfolioBuilder;
//!
//! // Use predefined test symbols
//! let symbol = TEST_EQUITY_1; // "TEST_EQ_001"
//!
//! // Generate symbols dynamically
//! let forex_symbol = generate_test_symbol(AssetClass::Currencies);
//!
//! // Use builders for complex data
//! let portfolio = PortfolioBuilder::new()
//!     .with_symbol(TEST_EQUITY_1)
//!     .with_quantity(Decimal::from(100))
//!     .build();
//! ```

use std::collections::HashMap;
use std::sync::{
    atomic::{AtomicU16, Ordering},
    Arc,
};
use std::time::Duration;

use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde_json::json;
use tokio::sync::RwLock;
use uuid::Uuid;

// Import TLI types explicitly (tli crate is available as dependency)
use fxt::error::FxtResult;

// Re-export sub-modules for easy access
pub mod builders;
pub mod helpers;
pub mod mock_services;
pub mod scenarios;
pub mod test_config;
pub mod test_data;
pub mod test_database;

// =============================================================================
// TEST SYMBOLS - PRODUCTION READY CONSTANTS
// =============================================================================

/// Test equity symbols for consistent testing across all modules
pub const TEST_EQUITY_1: &str = "TEST_EQ_001";
pub const TEST_EQUITY_2: &str = "TEST_EQ_002";
pub const TEST_EQUITY_3: &str = "TEST_EQ_003";
pub const TEST_EQUITY_LARGE_CAP: &str = "TEST_EQ_LC_001";
pub const TEST_EQUITY_MID_CAP: &str = "TEST_EQ_MC_001";
pub const TEST_EQUITY_SMALL_CAP: &str = "TEST_EQ_SC_001";

/// Test forex currency pairs for FX trading tests
pub const TEST_FOREX_1: &str = "TEST_FX_EURUSD";
pub const TEST_FOREX_2: &str = "TEST_FX_GBPUSD";
pub const TEST_FOREX_3: &str = "TEST_FX_USDJPY";
pub const TEST_FOREX_EXOTIC: &str = "TEST_FX_USDTRY";

/// Test futures contracts for derivatives testing
pub const TEST_FUTURE_1: &str = "TEST_FUT_ES001";
pub const TEST_FUTURE_2: &str = "TEST_FUT_NQ001";
pub const TEST_FUTURE_OIL: &str = "TEST_FUT_CL001";
pub const TEST_FUTURE_GOLD: &str = "TEST_FUT_GC001";

/// Test bond symbols for fixed income testing
pub const TEST_BOND_1: &str = "TEST_BOND_UST10Y";
pub const TEST_BOND_2: &str = "TEST_BOND_UST2Y";
pub const TEST_BOND_CORP: &str = "TEST_BOND_CORP_AAA";
pub const TEST_BOND_HIGH_YIELD: &str = "TEST_BOND_HY_001";

/// Test commodity symbols
pub const TEST_COMMODITY_1: &str = "TEST_COMM_GOLD";
pub const TEST_COMMODITY_2: &str = "TEST_COMM_SILVER";
pub const TEST_COMMODITY_OIL: &str = "TEST_COMM_OIL";
pub const TEST_COMMODITY_GAS: &str = "TEST_COMM_NATGAS";

/// Test cryptocurrency symbols
pub const TEST_CRYPTO_1: &str = "TEST_CRYPTO_BTC";
pub const TEST_CRYPTO_2: &str = "TEST_CRYPTO_ETH";
pub const TEST_CRYPTO_ALT: &str = "TEST_CRYPTO_ADA";

/// Test option symbols
pub const TEST_OPTION_CALL: &str = "TEST_OPT_CALL_001";
pub const TEST_OPTION_PUT: &str = "TEST_OPT_PUT_001";

/// Comprehensive symbol collections for batch testing
pub const ALL_TEST_EQUITIES: &[&str] = &[
    TEST_EQUITY_1,
    TEST_EQUITY_2,
    TEST_EQUITY_3,
    TEST_EQUITY_LARGE_CAP,
    TEST_EQUITY_MID_CAP,
    TEST_EQUITY_SMALL_CAP,
];

pub const ALL_TEST_FX_PAIRS: &[&str] =
    &[TEST_FOREX_1, TEST_FOREX_2, TEST_FOREX_3, TEST_FOREX_EXOTIC];

pub const ALL_TEST_FUTURES: &[&str] = &[
    TEST_FUTURE_1,
    TEST_FUTURE_2,
    TEST_FUTURE_OIL,
    TEST_FUTURE_GOLD,
];

pub const ALL_TEST_BONDS: &[&str] = &[
    TEST_BOND_1,
    TEST_BOND_2,
    TEST_BOND_CORP,
    TEST_BOND_HIGH_YIELD,
];

pub const ALL_TEST_COMMODITIES: &[&str] = &[
    TEST_COMMODITY_1,
    TEST_COMMODITY_2,
    TEST_COMMODITY_OIL,
    TEST_COMMODITY_GAS,
];

pub const ALL_TEST_CRYPTOS: &[&str] = &[TEST_CRYPTO_1, TEST_CRYPTO_2, TEST_CRYPTO_ALT];

/// All test symbols combined for comprehensive testing
pub const ALL_TEST_SYMBOLS: &[&str] = &[
    // Equities
    TEST_EQUITY_1,
    TEST_EQUITY_2,
    TEST_EQUITY_3,
    TEST_EQUITY_LARGE_CAP,
    TEST_EQUITY_MID_CAP,
    TEST_EQUITY_SMALL_CAP,
    // Forex
    TEST_FOREX_1,
    TEST_FOREX_2,
    TEST_FOREX_3,
    TEST_FOREX_EXOTIC,
    // Futures
    TEST_FUTURE_1,
    TEST_FUTURE_2,
    TEST_FUTURE_OIL,
    TEST_FUTURE_GOLD,
    // Bonds
    TEST_BOND_1,
    TEST_BOND_2,
    TEST_BOND_CORP,
    TEST_BOND_HIGH_YIELD,
    // Commodities
    TEST_COMMODITY_1,
    TEST_COMMODITY_2,
    TEST_COMMODITY_OIL,
    TEST_COMMODITY_GAS,
    // Crypto
    TEST_CRYPTO_1,
    TEST_CRYPTO_2,
    TEST_CRYPTO_ALT,
    // Options
    TEST_OPTION_CALL,
    TEST_OPTION_PUT,
];

// =============================================================================
// TEST SYMBOL GENERATORS
// =============================================================================

// Use local AssetClass definition for fixtures
/// Asset class for test symbol generation
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AssetClass {
    Equities,
    Currencies,
    Derivatives,
    FixedIncome,
    Commodities,
    Cash,
    Alternatives,
}

impl std::fmt::Display for AssetClass {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AssetClass::Equities => write!(f, "Equities"),
            AssetClass::Currencies => write!(f, "Currencies"),
            AssetClass::Derivatives => write!(f, "Derivatives"),
            AssetClass::FixedIncome => write!(f, "FixedIncome"),
            AssetClass::Commodities => write!(f, "Commodities"),
            AssetClass::Cash => write!(f, "Cash"),
            AssetClass::Alternatives => write!(f, "Alternatives"),
        }
    }
}

/// Instrument type for test fixtures
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InstrumentType {
    Equity,
    Currency,
    Future,
    Option,
    Bond,
    Commodity,
    Crypto,
    Swap,
    CDS,
}

impl std::fmt::Display for InstrumentType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            InstrumentType::Equity => write!(f, "Equity"),
            InstrumentType::Currency => write!(f, "Currency"),
            InstrumentType::Future => write!(f, "Future"),
            InstrumentType::Option => write!(f, "Option"),
            InstrumentType::Bond => write!(f, "Bond"),
            InstrumentType::Commodity => write!(f, "Commodity"),
            InstrumentType::Crypto => write!(f, "Crypto"),
            InstrumentType::Swap => write!(f, "Swap"),
            InstrumentType::CDS => write!(f, "CDS"),
        }
    }
}

/// Market sector for test fixtures
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MarketSector {
    Technology,
    Healthcare,
    Financials,
    Energy,
    ConsumerDiscretionary,
    ConsumerStaples,
    Industrials,
    Materials,
    Utilities,
    RealEstate,
    Communication,
}

/// Test fixture Instrument type
#[derive(Debug, Clone)]
pub struct Instrument {
    pub id: Uuid,
    pub symbol: String,
    pub isin: Option<String>,
    pub cusip: Option<String>,
    pub bloomberg_id: Option<String>,
    pub reuters_id: Option<String>,
    pub name: String,
    pub instrument_type: InstrumentType,
    pub asset_class: AssetClass,
    pub sector: Option<MarketSector>,
    pub currency: String,
    pub exchange: Option<String>,
    pub tick_size: Option<Decimal>,
    pub lot_size: Option<Decimal>,
    pub multiplier: Option<Decimal>,
    pub maturity_date: Option<DateTime<Utc>>,
    pub strike_price: Option<Decimal>,
    pub option_type: Option<String>,
    pub underlying_symbol: Option<String>,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub metadata: serde_json::Value,
}

/// Test fixture Portfolio type
#[derive(Debug, Clone)]
pub struct Portfolio {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub base_currency: String,
    pub portfolio_type: String,
    pub inception_date: DateTime<Utc>,
    pub manager_id: String,
    pub benchmark: Option<String>,
    pub risk_budget: Option<Decimal>,
    pub var_limit: Option<Decimal>,
    pub max_drawdown_limit: Option<Decimal>,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub metadata: serde_json::Value,
}

/// Test fixture Counterparty type
#[derive(Debug, Clone)]
pub struct Counterparty {
    pub id: String,
    pub name: String,
    pub counterparty_type: String,
    pub country: String,
    pub credit_rating: Option<String>,
    pub lei_code: Option<String>,
    pub parent_company: Option<String>,
    pub is_active: bool,
    pub exposure_limit: Option<Decimal>,
    pub margin_requirement: Option<Decimal>,
    pub netting_agreement: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub metadata: serde_json::Value,
}

/// Generate a test symbol for the specified asset class
pub fn generate_test_symbol(asset_class: AssetClass) -> String {
    use std::sync::atomic::{AtomicUsize, Ordering};

    static EQUITY_COUNTER: AtomicUsize = AtomicUsize::new(1000);
    static FX_COUNTER: AtomicUsize = AtomicUsize::new(1000);
    static FUTURES_COUNTER: AtomicUsize = AtomicUsize::new(1000);
    static BOND_COUNTER: AtomicUsize = AtomicUsize::new(1000);
    static COMMODITY_COUNTER: AtomicUsize = AtomicUsize::new(1000);
    static CASH_COUNTER: AtomicUsize = AtomicUsize::new(1000);
    static ALT_COUNTER: AtomicUsize = AtomicUsize::new(1000);

    match asset_class {
        AssetClass::Equities => {
            let id = EQUITY_COUNTER.fetch_add(1, Ordering::Relaxed);
            format!("TEST_EQ_{:03}", id)
        },
        AssetClass::Currencies => {
            let id = FX_COUNTER.fetch_add(1, Ordering::Relaxed);
            let pairs = ["EURUSD", "GBPUSD", "USDJPY", "AUDUSD", "USDCAD"];
            let pair = pairs[id % pairs.len()];
            format!("TEST_FX_{}", pair)
        },
        AssetClass::Derivatives => {
            let id = FUTURES_COUNTER.fetch_add(1, Ordering::Relaxed);
            format!("TEST_FUT_{:03}", id)
        },
        AssetClass::FixedIncome => {
            let id = BOND_COUNTER.fetch_add(1, Ordering::Relaxed);
            format!("TEST_BOND_{:03}", id)
        },
        AssetClass::Commodities => {
            let id = COMMODITY_COUNTER.fetch_add(1, Ordering::Relaxed);
            format!("TEST_COMM_{:03}", id)
        },
        AssetClass::Cash => {
            let id = CASH_COUNTER.fetch_add(1, Ordering::Relaxed);
            format!("TEST_CASH_{:03}", id)
        },
        AssetClass::Alternatives => {
            let id = ALT_COUNTER.fetch_add(1, Ordering::Relaxed);
            // Assume crypto for alternatives
            format!("TEST_CRYPTO_{:03}", id)
        },
    }
}

/// Generate multiple test symbols for an asset class
pub fn generate_test_symbols(asset_class: AssetClass, count: usize) -> Vec<String> {
    (0..count)
        .map(|_| generate_test_symbol(asset_class))
        .collect()
}

/// Generate a random test symbol from all available symbols
pub fn generate_random_test_symbol() -> &'static str {
    use rand::seq::SliceRandom;
    let mut rng = rand::thread_rng();
    ALL_TEST_SYMBOLS.choose(&mut rng).unwrap_or(&TEST_EQUITY_1)
}

// =============================================================================
// TEST PRICE CONSTANTS
// =============================================================================

/// Standard test prices for consistent testing
pub const TEST_PRICE_EQUITY_BASE: f64 = 100.0;
pub const TEST_PRICE_FX_BASE: f64 = 1.0;
pub const TEST_PRICE_FUTURES_BASE: f64 = 4000.0;
pub const TEST_PRICE_BOND_BASE: f64 = 100.0;
pub const TEST_PRICE_COMMODITY_BASE: f64 = 2000.0;
pub const TEST_PRICE_CRYPTO_BASE: f64 = 50000.0;

/// Test quantity constants
pub const TEST_QUANTITY_SMALL: i64 = 100;
pub const TEST_QUANTITY_MEDIUM: i64 = 1000;
pub const TEST_QUANTITY_LARGE: i64 = 10000;

/// Test monetary amounts
pub const TEST_AMOUNT_SMALL: f64 = 10000.0;
pub const TEST_AMOUNT_MEDIUM: f64 = 100000.0;
pub const TEST_AMOUNT_LARGE: f64 = 1000000.0;

// =============================================================================
// TEST PORTFOLIO IDENTIFIERS
// =============================================================================

/// Standard test portfolio IDs
pub const TEST_PORTFOLIO_1: &str = "TEST_PORTFOLIO_001";
pub const TEST_PORTFOLIO_2: &str = "TEST_PORTFOLIO_002";
pub const TEST_PORTFOLIO_HEDGE: &str = "TEST_PORTFOLIO_HEDGE";
pub const TEST_PORTFOLIO_LONG_ONLY: &str = "TEST_PORTFOLIO_LONG";
pub const TEST_PORTFOLIO_MARKET_NEUTRAL: &str = "TEST_PORTFOLIO_NEUTRAL";

/// Test strategy identifiers
pub const TEST_STRATEGY_MOMENTUM: &str = "TEST_STRAT_MOMENTUM";
pub const TEST_STRATEGY_MEAN_REVERT: &str = "TEST_STRAT_MEAN_REV";
pub const TEST_STRATEGY_ARBITRAGE: &str = "TEST_STRAT_ARBITRAGE";

// =============================================================================
// ORIGINAL FIXTURES INTEGRATION
// =============================================================================

/// Integration test configuration
#[derive(Debug, Clone)]
pub struct IntegrationTestConfig {
    // Performance requirements
    pub max_latency_ns: u64,
    pub max_db_latency_ns: u64,
    pub max_risk_latency_ns: u64,
    pub max_ml_inference_latency_ns: u64,
    pub max_init_latency_ns: u64,
    pub min_throughput_ops_per_sec: f64,
    pub min_db_throughput_ops_per_sec: f64,
    pub min_backtest_throughput: f64,

    // Test parameters
    pub request_timeout_ms: u64,
    pub max_retry_attempts: u32,
    pub circuit_breaker_threshold: u32,
    pub concurrent_order_count: usize,
    pub parallel_backtest_count: usize,
    pub concurrent_operation_count: usize,
    pub batch_size: usize,
    pub stream_buffer_size: usize,
    pub stress_test_duration_secs: u64,

    // Database configuration
    pub test_db_url: String,
    pub test_db_max_connections: u32,
    pub enable_database_cleanup: bool,

    // Mock service configuration
    pub mock_service_latency_ms: u64,
    pub mock_failure_rate: f64,
    pub enable_chaos_testing: bool,
}

impl Default for IntegrationTestConfig {
    fn default() -> Self {
        Self {
            // HFT Performance requirements
            max_latency_ns: 50_000,                 // 50µs max latency
            max_db_latency_ns: 100_000,             // 100µs max DB latency
            max_risk_latency_ns: 25_000,            // 25µs max risk validation
            max_ml_inference_latency_ns: 50_000,    // 50µs max ML inference
            max_init_latency_ns: 1_000_000,         // 1ms max initialization
            min_throughput_ops_per_sec: 10_000.0,   // 10K ops/sec minimum
            min_db_throughput_ops_per_sec: 5_000.0, // 5K DB ops/sec minimum
            min_backtest_throughput: 10.0,          // 10 backtests/sec minimum

            // Test parameters
            request_timeout_ms: 5_000, // 5 second timeout
            max_retry_attempts: 3,
            circuit_breaker_threshold: 5,
            concurrent_order_count: 100,
            parallel_backtest_count: 20,
            concurrent_operation_count: 50,
            batch_size: 1000,
            stream_buffer_size: 10_000,
            stress_test_duration_secs: 30,

            // Database configuration
            test_db_url: std::env::var("TEST_DATABASE_URL").unwrap_or_else(|_| {
                "postgresql://foxhunt_test:test_password@localhost:5432/foxhunt_test".to_string()
            }),
            test_db_max_connections: 20,
            enable_database_cleanup: true,

            // Mock service configuration
            mock_service_latency_ms: 10,
            mock_failure_rate: 0.01, // 1% failure rate
            enable_chaos_testing: false,
        }
    }
}

/// Base test result structure
#[derive(Debug, Clone)]
pub struct TestResult {
    pub name: String,
    pub passed: bool,
    pub execution_time: Duration,
    pub assertions: Vec<Assertion>,
    pub errors: Vec<String>,
    pub metadata: HashMap<String, serde_json::Value>,
}

impl TestResult {
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            passed: false,
            execution_time: Duration::default(),
            assertions: Vec::new(),
            errors: Vec::new(),
            metadata: HashMap::new(),
        }
    }

    pub fn add_assertion(&mut self, description: &str, passed: bool) {
        self.assertions.push(Assertion {
            description: description.to_string(),
            passed,
        });
    }

    pub fn add_error(&mut self, error: String) {
        self.errors.push(error);
    }

    pub fn set_passed(&mut self, passed: bool) {
        self.passed = passed;
    }
}

/// Individual test assertion
#[derive(Debug, Clone)]
pub struct Assertion {
    pub description: String,
    pub passed: bool,
}

/// Test suite containing multiple test results
#[derive(Debug, Clone)]
pub struct TestSuite {
    pub name: String,
    pub tests: Vec<TestResult>,
    pub passed_tests: usize,
    pub total_tests: usize,
    pub passed: bool,
    pub execution_time: Duration,
    pub metadata: HashMap<String, serde_json::Value>,
}

impl TestSuite {
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            tests: Vec::new(),
            passed_tests: 0,
            total_tests: 0,
            passed: false,
            execution_time: Duration::default(),
            metadata: HashMap::new(),
        }
    }

    pub fn add_test_result(&mut self, test: TestResult) {
        if test.passed {
            self.passed_tests += 1;
        }
        self.total_tests += 1;
        self.tests.push(test);
    }

    pub fn set_passed(&mut self, passed: bool) {
        self.passed = passed;
    }
}

/// Port manager for test services
#[derive(Debug)]
pub struct TestPortManager {
    next_port: AtomicU16,
    allocated_ports: RwLock<Vec<u16>>,
}

impl TestPortManager {
    pub fn new() -> Self {
        Self {
            next_port: AtomicU16::new(50000), // Start from port 50000
            allocated_ports: RwLock::new(Vec::new()),
        }
    }

    pub async fn allocate_port(&self) -> u16 {
        loop {
            let port = self.next_port.fetch_add(1, Ordering::Relaxed);
            if port > 65000 {
                // Reset if we've used too many ports
                self.next_port.store(50000, Ordering::Relaxed);
                continue;
            }

            // Check if port is available
            if let Ok(listener) = tokio::net::TcpListener::bind(format!("127.0.0.1:{}", port)).await
            {
                drop(listener); // Release the port
                self.allocated_ports.write().await.push(port);
                return port;
            }
        }
    }

    pub async fn release_port(&self, port: u16) {
        let mut allocated = self.allocated_ports.write().await;
        if let Some(pos) = allocated.iter().position(|&p| p == port) {
            allocated.remove(pos);
        }
    }
}

impl Default for TestPortManager {
    fn default() -> Self {
        Self::new()
    }
}

// Global test port manager instance
lazy_static::lazy_static! {
    pub static ref TEST_PORT_MANAGER: TestPortManager = TestPortManager::new();
}

// TestEventPublisher removed — event streaming moved to gRPC server-streaming in api service

/// Performance metrics collector for tests
#[derive(Debug, Default)]
pub struct TestMetricsCollector {
    latency_measurements: RwLock<HashMap<String, Vec<u64>>>,
    throughput_measurements: RwLock<HashMap<String, Vec<f64>>>,
    error_counts: RwLock<HashMap<String, u64>>,
    custom_metrics: RwLock<HashMap<String, serde_json::Value>>,
}

impl TestMetricsCollector {
    pub fn new() -> Self {
        Self::default()
    }

    pub async fn record_latency(&self, operation: &str, latency_ns: u64) {
        let mut latencies = self.latency_measurements.write().await;
        latencies
            .entry(operation.to_string())
            .or_insert_with(Vec::new)
            .push(latency_ns);
    }

    pub async fn record_throughput(&self, operation: &str, ops_per_sec: f64) {
        let mut throughputs = self.throughput_measurements.write().await;
        throughputs
            .entry(operation.to_string())
            .or_insert_with(Vec::new)
            .push(ops_per_sec);
    }

    pub async fn record_error(&self, operation: &str) {
        let mut errors = self.error_counts.write().await;
        *errors.entry(operation.to_string()).or_insert(0) += 1;
    }

    pub async fn record_custom_metric(&self, name: &str, value: serde_json::Value) {
        let mut metrics = self.custom_metrics.write().await;
        metrics.insert(name.to_string(), value);
    }

    pub async fn get_latency_stats(&self, operation: &str) -> Option<LatencyStats> {
        let latencies = self.latency_measurements.read().await;
        if let Some(measurements) = latencies.get(operation) {
            if measurements.is_empty() {
                return None;
            }

            let mut sorted = measurements.clone();
            sorted.sort_unstable();

            let len = sorted.len();
            let avg = sorted.iter().sum::<u64>() / len as u64;
            let p50 = sorted[len * 50 / 100];
            let p95 = sorted[len * 95 / 100];
            let p99 = sorted[len * 99 / 100];
            let max = sorted[len - 1];

            Some(LatencyStats {
                avg,
                p50,
                p95,
                p99,
                max,
            })
        } else {
            None
        }
    }

    pub async fn get_summary(&self) -> serde_json::Value {
        let latencies = self.latency_measurements.read().await;
        let throughputs = self.throughput_measurements.read().await;
        let errors = self.error_counts.read().await;
        let custom = self.custom_metrics.read().await;

        let mut latency_summary = serde_json::Map::new();
        for (operation, measurements) in latencies.iter() {
            if let Some(stats) = self.get_latency_stats(operation).await {
                latency_summary.insert(
                    operation.to_string(),
                    json!({
                        "count": measurements.len(),
                        "avg_ns": stats.avg,
                        "p50_ns": stats.p50,
                        "p95_ns": stats.p95,
                        "p99_ns": stats.p99,
                        "max_ns": stats.max
                    }),
                );
            }
        }

        let mut throughput_summary = serde_json::Map::new();
        for (operation, measurements) in throughputs.iter() {
            if !measurements.is_empty() {
                let avg = measurements.iter().sum::<f64>() / measurements.len() as f64;
                let max = measurements.iter().fold(0.0f64, |a, &b| a.max(b));
                let min = measurements.iter().fold(f64::INFINITY, |a, &b| a.min(b));

                throughput_summary.insert(
                    operation.to_string(),
                    json!({
                        "count": measurements.len(),
                        "avg_ops_per_sec": avg,
                        "max_ops_per_sec": max,
                        "min_ops_per_sec": min
                    }),
                );
            }
        }

        json!({
            "latencies": latency_summary,
            "throughput": throughput_summary,
            "errors": errors.clone(),
            "custom_metrics": custom.clone()
        })
    }
}

/// Latency statistics structure
#[derive(Debug, Clone)]
pub struct LatencyStats {
    pub avg: u64,
    pub p50: u64,
    pub p95: u64,
    pub p99: u64,
    pub max: u64,
}

/// Test environment setup and cleanup
pub struct TestEnvironment {
    pub config: IntegrationTestConfig,
    pub metrics: Arc<TestMetricsCollector>,
    pub port_manager: Arc<TestPortManager>,
    cleanup_tasks: Vec<
        Box<
            dyn Fn() -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>
                + Send
                + Sync,
        >,
    >,
}

impl std::fmt::Debug for TestEnvironment {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TestEnvironment")
            .field("config", &self.config)
            .field("metrics", &self.metrics)
            .field("port_manager", &self.port_manager)
            .field(
                "cleanup_tasks",
                &format!("<{} cleanup tasks>", self.cleanup_tasks.len()),
            )
            .finish()
    }
}

impl TestEnvironment {
    pub async fn new(config: IntegrationTestConfig) -> FxtResult<Self> {
        let metrics = Arc::new(TestMetricsCollector::new());
        let port_manager = Arc::new(TestPortManager::new());

        Ok(Self {
            config,
            metrics,
            port_manager,
            cleanup_tasks: Vec::new(),
        })
    }

    pub fn add_cleanup_task<F, Fut>(&mut self, task: F)
    where
        F: Fn() -> Fut + Send + Sync + 'static,
        Fut: std::future::Future<Output = ()> + Send + 'static,
    {
        let boxed_task = Box::new(move || {
            Box::pin(task()) as std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send>>
        });
        self.cleanup_tasks.push(boxed_task);
    }

    pub async fn cleanup(self) {
        for task in self.cleanup_tasks {
            task().await;
        }
    }
}

// =============================================================================
// UTILITY FUNCTIONS
// =============================================================================

/// Get test symbol for specific asset class by index
pub fn get_test_symbol_by_class(asset_class: AssetClass, index: usize) -> Option<&'static str> {
    match asset_class {
        AssetClass::Equities => ALL_TEST_EQUITIES.get(index).copied(),
        AssetClass::Currencies => ALL_TEST_FX_PAIRS.get(index).copied(),
        AssetClass::Derivatives => ALL_TEST_FUTURES.get(index).copied(),
        AssetClass::FixedIncome => ALL_TEST_BONDS.get(index).copied(),
        AssetClass::Commodities => ALL_TEST_COMMODITIES.get(index).copied(),
        AssetClass::Alternatives => ALL_TEST_CRYPTOS.get(index).copied(),
        AssetClass::Cash => Some("TEST_CASH_USD"),
    }
}

/// Get all symbols for a specific asset class
pub fn get_symbols_for_asset_class(asset_class: AssetClass) -> &'static [&'static str] {
    match asset_class {
        AssetClass::Equities => ALL_TEST_EQUITIES,
        AssetClass::Currencies => ALL_TEST_FX_PAIRS,
        AssetClass::Derivatives => ALL_TEST_FUTURES,
        AssetClass::FixedIncome => ALL_TEST_BONDS,
        AssetClass::Commodities => ALL_TEST_COMMODITIES,
        AssetClass::Alternatives => ALL_TEST_CRYPTOS,
        AssetClass::Cash => &["TEST_CASH_USD"],
    }
}

/// Generate test price for symbol based on asset class
pub fn get_test_price_for_symbol(symbol: &str) -> f64 {
    if symbol.starts_with("TEST_EQ_") {
        TEST_PRICE_EQUITY_BASE
    } else if symbol.starts_with("TEST_FX_") {
        TEST_PRICE_FX_BASE
    } else if symbol.starts_with("TEST_FUT_") {
        TEST_PRICE_FUTURES_BASE
    } else if symbol.starts_with("TEST_BOND_") {
        TEST_PRICE_BOND_BASE
    } else if symbol.starts_with("TEST_COMM_") {
        TEST_PRICE_COMMODITY_BASE
    } else if symbol.starts_with("TEST_CRYPTO_") {
        TEST_PRICE_CRYPTO_BASE
    } else {
        100.0 // Default price
    }
}

/// Create test symbol metadata
pub fn create_test_symbol_metadata(symbol: &str) -> serde_json::Value {
    json!({
        "symbol": symbol,
        "test_data": true,
        "created_at": Utc::now(),
        "fixture_version": "1.0"
    })
}

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

    #[test]
    fn test_symbol_generation() {
        // Test predefined symbols
        assert_eq!(TEST_EQUITY_1, "TEST_EQ_001");
        assert_eq!(TEST_FOREX_1, "TEST_FX_EURUSD");
        assert_eq!(TEST_FUTURE_1, "TEST_FUT_ES001");

        // Test dynamic generation
        let equity_symbol = generate_test_symbol(AssetClass::Equities);
        assert!(equity_symbol.starts_with("TEST_EQ_"));

        let fx_symbol = generate_test_symbol(AssetClass::Currencies);
        assert!(fx_symbol.starts_with("TEST_FX_"));
    }

    #[test]
    #[allow(clippy::const_is_empty)]
    fn test_symbol_collections() {
        assert!(!ALL_TEST_SYMBOLS.is_empty());
        assert!(ALL_TEST_SYMBOLS.contains(&TEST_EQUITY_1));
        assert!(ALL_TEST_SYMBOLS.contains(&TEST_FOREX_1));

        // Test asset class specific collections
        assert!(!ALL_TEST_EQUITIES.is_empty());
        assert!(!ALL_TEST_FX_PAIRS.is_empty());
        assert!(!ALL_TEST_FUTURES.is_empty());
    }

    #[test]
    fn test_price_generation() {
        assert_eq!(
            get_test_price_for_symbol(TEST_EQUITY_1),
            TEST_PRICE_EQUITY_BASE
        );
        assert_eq!(get_test_price_for_symbol(TEST_FOREX_1), TEST_PRICE_FX_BASE);
        assert_eq!(
            get_test_price_for_symbol(TEST_FUTURE_1),
            TEST_PRICE_FUTURES_BASE
        );
    }

    #[test]
    fn test_symbol_metadata() {
        let metadata = create_test_symbol_metadata(TEST_EQUITY_1);
        assert_eq!(metadata["symbol"], TEST_EQUITY_1);
        assert_eq!(metadata["test_data"], true);
    }
}
