#![allow(clippy::mixed_attributes_style)] // Generated protobuf code uses mixed attributes
#![allow(clippy::empty_line_after_doc_comments)] // Doc comment formatting acceptable
#![allow(clippy::redundant_pattern_matching)] // Explicit pattern matching preferred in tests
#![allow(clippy::manual_clamp)] // Explicit min/max preferred in test code
#![allow(clippy::unnecessary_to_owned)] // Explicit to_string in test HTML generation
//! Foxhunt E2E Testing Framework
//!
//! Comprehensive End-to-End testing framework for the Foxhunt High-Frequency Trading system.
//! Tests complete integration between TLI client, Trading Service, Backtesting Service,
//! ML Training Service, database interactions, and complete trading workflows.

pub mod clients;
pub mod database;
pub mod framework;
pub mod ml_pipeline;
pub mod performance;
pub mod proto;
pub mod services;
pub mod utils;
pub mod workflows;

// Re-export commonly used types from workflows
pub use workflows::WorkflowTestResult;

// Re-export E2ETestFramework for public access in tests
pub use crate::framework::E2ETestFramework;

use anyhow::Result;
use std::future::Future;
use std::pin::Pin;

/// E2E Test Result type
pub type E2ETestResult<T = ()> = Result<T>;

/// Async test function trait
pub type E2ETestFn = fn(E2ETestFramework) -> Pin<Box<dyn Future<Output = E2ETestResult> + Send>>;

/// E2E test macro for creating standardized tests
///
/// This macro provides:
/// - Automatic service orchestration
///
/// - Test framework initialization
/// - Proper cleanup on test completion or failure
///
/// - Performance monitoring
/// - Comprehensive logging
///
/// # Example
///
/// ```rust
/// use foxhunt_e2e::{e2e_test, E2ETestFramework, E2ETestResult};
/// use std::sync::Arc;
///
/// // Basic usage with owned framework
/// e2e_test!(test_basic_trading_flow, |framework: E2ETestFramework| async {
///     let trading_client = framework.get_trading_client().await?;
///
///     let response = trading_client.get_account_info().await?;
///     assert!(response.total_value > 0.0);
///
///     Ok(())
/// });
///
/// // Usage with Arc<E2ETestFramework> and async move
/// e2e_test!(test_with_arc, |framework: Arc<E2ETestFramework>| async move {
///     let workflows = SomeWorkflow::new(framework);
///     workflows.test_something().await?;
///
///     Ok(())
/// });
/// ```
#[macro_export]
macro_rules! e2e_test {
    // Pattern 1: async move closure with mut (captures framework by value)
    ($test_name:ident, |mut $framework:ident: $framework_type:ty| async move $test_body:block) => {
        #[tokio::test]
        async fn $test_name() -> $crate::E2ETestResult {
            use tracing::{info, error, warn};
            use std::time::Instant;

            info!("🚀 Starting E2E test: {}", stringify!($test_name));
            let start_time = Instant::now();

            // Initialize the test framework
            let mut framework_instance = match $crate::framework::E2ETestFramework::new().await {
                Ok(framework) => {
                    info!("✅ E2E test framework initialized successfully");
                    framework
                }
                Err(e) => {
                    error!("❌ Failed to initialize E2E test framework: {}", e);
                    return Err(e);
                }
            };

            // Start services if needed
            if let Err(e) = framework_instance.start_services().await {
                error!("❌ Failed to start services: {}", e);
                return Err(e);
            }

            // Execute the test body (async move closure)
            let test_result: $crate::E2ETestResult = {
                let mut $framework: $framework_type = framework_instance;
                (async move $test_body).await
            };

            // Cleanup and report results
            match &test_result {
                Ok(_) => {
                    let duration = start_time.elapsed();
                    info!("✅ E2E test {} completed successfully in {:?}",
                          stringify!($test_name), duration);
                }
                Err(e) => {
                    let duration = start_time.elapsed();
                    error!("❌ E2E test {} failed after {:?}: {}",
                           stringify!($test_name), duration, e);
                }
            }

            test_result
        }
    };

    // Pattern 2: async move closure (captures framework by value)
    ($test_name:ident, |$framework:ident: $framework_type:ty| async move $test_body:block) => {
        #[tokio::test]
        async fn $test_name() -> $crate::E2ETestResult {
            use tracing::{info, error, warn};
            use std::time::Instant;

            info!("🚀 Starting E2E test: {}", stringify!($test_name));
            let start_time = Instant::now();

            // Initialize the test framework
            let mut framework_instance = match $crate::framework::E2ETestFramework::new().await {
                Ok(framework) => {
                    info!("✅ E2E test framework initialized successfully");
                    framework
                }
                Err(e) => {
                    error!("❌ Failed to initialize E2E test framework: {}", e);
                    return Err(e);
                }
            };

            // Start services if needed
            if let Err(e) = framework_instance.start_services().await {
                error!("❌ Failed to start services: {}", e);
                return Err(e);
            }

            // Execute the test body (async move closure)
            let test_result: $crate::E2ETestResult = {
                let $framework: $framework_type = std::sync::Arc::new(framework_instance);
                (async move $test_body).await
            };

            // Cleanup and report results
            match &test_result {
                Ok(_) => {
                    let duration = start_time.elapsed();
                    info!("✅ E2E test {} completed successfully in {:?}",
                          stringify!($test_name), duration);
                }
                Err(e) => {
                    let duration = start_time.elapsed();
                    error!("❌ E2E test {} failed after {:?}: {}",
                           stringify!($test_name), duration, e);
                }
            }

            test_result
        }
    };

    // Pattern 3: async closure with mut (borrows framework)
    ($test_name:ident, |mut $framework:ident: $framework_type:ty| async $test_body:block) => {
        #[tokio::test]
        async fn $test_name() -> $crate::E2ETestResult {
            use tracing::{info, error, warn};
            use std::time::Instant;

            info!("🚀 Starting E2E test: {}", stringify!($test_name));
            let start_time = Instant::now();

            // Initialize the test framework
            let mut $framework = match $crate::framework::E2ETestFramework::new().await {
                Ok(framework) => {
                    info!("✅ E2E test framework initialized successfully");
                    framework
                }
                Err(e) => {
                    error!("❌ Failed to initialize E2E test framework: {}", e);
                    return Err(e);
                }
            };

            // Start services if needed
            if let Err(e) = $framework.start_services().await {
                error!("❌ Failed to start services: {}", e);
                return Err(e);
            }

            // Execute the test body (async closure)
            let test_result: $crate::E2ETestResult = (async $test_body).await;

            // Cleanup and report results
            match &test_result {
                Ok(_) => {
                    let duration = start_time.elapsed();
                    info!("✅ E2E test {} completed successfully in {:?}",
                          stringify!($test_name), duration);
                }
                Err(e) => {
                    let duration = start_time.elapsed();
                    error!("❌ E2E test {} failed after {:?}: {}",
                           stringify!($test_name), duration, e);
                }
            }

            // Stop services and cleanup
            if let Err(e) = $framework.stop_services().await {
                warn!("⚠️ Failed to stop services cleanly: {}", e);
            }

            test_result
        }
    };

    // Pattern 4: async closure (borrows framework)
    ($test_name:ident, |$framework:ident: $framework_type:ty| async $test_body:block) => {
        #[tokio::test]
        async fn $test_name() -> $crate::E2ETestResult {
            use tracing::{info, error, warn};
            use std::time::Instant;

            info!("🚀 Starting E2E test: {}", stringify!($test_name));
            let start_time = Instant::now();

            // Initialize the test framework
            let mut $framework = match $crate::framework::E2ETestFramework::new().await {
                Ok(framework) => {
                    info!("✅ E2E test framework initialized successfully");
                    framework
                }
                Err(e) => {
                    error!("❌ Failed to initialize E2E test framework: {}", e);
                    return Err(e);
                }
            };

            // Start services if needed
            if let Err(e) = $framework.start_services().await {
                error!("❌ Failed to start services: {}", e);
                return Err(e);
            }

            // Execute the test body (async closure)
            let test_result: $crate::E2ETestResult = (async $test_body).await;

            // Cleanup and report results
            match &test_result {
                Ok(_) => {
                    let duration = start_time.elapsed();
                    info!("✅ E2E test {} completed successfully in {:?}",
                          stringify!($test_name), duration);
                }
                Err(e) => {
                    let duration = start_time.elapsed();
                    error!("❌ E2E test {} failed after {:?}: {}",
                           stringify!($test_name), duration, e);
                }
            }

            // Stop services and cleanup
            if let Err(e) = $framework.stop_services().await {
                warn!("⚠️ Failed to stop services cleanly: {}", e);
            }

            test_result
        }
    };
}

/// Utilities for test data generation and validation
pub mod test_utils {
    use crate::TestOrder;
    use anyhow::Result;
    use common::types::{Exchange, HftTimestamp, MarketTick, Price, Quantity, Symbol, TickType};
    use rand::Rng;
    use std::time::{SystemTime, UNIX_EPOCH};

    /// Generate realistic market data for testing
    pub fn generate_market_data(symbol: &str, count: usize) -> Vec<MarketTick> {
        let mut rng = rand::thread_rng();
        let mut price: f64 = 150.0; // Base price
        let mut ticks = Vec::with_capacity(count);

        for i in 0..count {
            price += rng.gen_range(-0.5..0.5);
            price = price.max(100.0).min(200.0); // Keep price in reasonable range

            let tick = MarketTick::new(
                Symbol::new(symbol.to_string()),
                Price::from_f64(price).unwrap_or(Price::ZERO),
                Quantity::from_f64(rng.gen_range(100..1000) as f64).unwrap(),
                TickType::Trade,
                Exchange::NASDAQ,
                i as u64,
            );

            match tick {
                Ok(t) => ticks.push(t),
                Err(_) => {
                    // Fallback to with_timestamp if new() fails
                    ticks.push(MarketTick::with_timestamp(
                        Symbol::new(symbol.to_string()),
                        Price::from_f64(price).unwrap_or(Price::ZERO),
                        Quantity::from_f64(rng.gen_range(100..1000) as f64).unwrap(),
                        HftTimestamp::from_nanos(
                            SystemTime::now()
                                .duration_since(UNIX_EPOCH)
                                .unwrap()
                                .as_nanos() as u64,
                        ),
                        TickType::Trade,
                        Exchange::NASDAQ,
                        i as u64,
                    ));
                },
            }
        }

        ticks
    }

    /// Generate realistic order for testing
    pub fn generate_test_order(symbol: &str) -> TestOrder {
        let mut rng = rand::thread_rng();

        TestOrder {
            symbol: symbol.to_string(),
            side: if rng.gen_bool(0.5) { "buy" } else { "sell" }.to_string(),
            order_type: "market".to_string(),
            quantity: rng.gen_range(100.0..1000.0),
            price: None, // Market order
            time_in_force: "day".to_string(),
        }
    }

    /// Wait for condition with timeout
    pub async fn wait_for_condition<F, Fut>(
        condition: F,
        timeout_secs: u64,
        check_interval_ms: u64,
    ) -> Result<()>
    where
        F: Fn() -> Fut,
        Fut: std::future::Future<Output = bool>,
    {
        use tokio::time::{sleep, Duration, Instant};

        let start = Instant::now();
        let timeout = Duration::from_secs(timeout_secs);
        let interval = Duration::from_millis(check_interval_ms);

        while start.elapsed() < timeout {
            if condition().await {
                return Ok(());
            }
            sleep(interval).await;
        }

        Err(anyhow::anyhow!(
            "Condition not met within {} seconds",
            timeout_secs
        ))
    }
}

/// Test data structures
#[derive(Debug, Clone)]
pub struct TestOrder {
    pub symbol: String,
    pub side: String,
    pub order_type: String,
    pub quantity: f64,
    pub price: Option<f64>,
    pub time_in_force: String,
}

#[derive(Debug, Clone)]
pub struct TestPosition {
    pub symbol: String,
    pub quantity: f64,
    pub average_price: f64,
    pub market_value: f64,
    pub unrealized_pnl: f64,
}

// test_utils module already defined above, no need to re-export

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

    #[tokio::test]
    #[ignore] // Requires running services (database, JWT, ML pipeline)
    async fn test_framework_initialization() {
        let framework = framework::E2ETestFramework::new().await;
        assert!(
            framework.is_ok(),
            "Framework should initialize successfully"
        );
    }

    #[test]
    fn test_data_generation() {
        let ticks = test_utils::generate_market_data("AAPL", 10);
        assert_eq!(ticks.len(), 10);
        assert!(ticks.iter().all(|t| t.symbol == "AAPL"));
        assert!(ticks.iter().all(|t| t.price > 100.0 && t.price < 200.0));
    }

    #[test]
    fn test_order_generation() {
        let order = test_utils::generate_test_order("MSFT");
        assert_eq!(order.symbol, "MSFT");
        assert!(order.side == "buy" || order.side == "sell");
        assert!(order.quantity >= 100.0 && order.quantity <= 1000.0);
    }
}
