//! Full Trading Cycle Performance Profiling
//!
//! This benchmark profiles the complete end-to-end trading flow:
//! 1. Order submission → TradingOperations::submit_order()
//! 2. Order validation → TradingOperations::validate_order()
//! 3. Execution routing → TradingOperations::process_execution()
//! 4. Audit trail persistence → AuditTrailService::log_event()
//! 5. Metrics collection → Prometheus recording
//!
//! HFT Performance Targets:
//! - Order submission: <50μs P99
//! - Order validation: <5μs P99
//! - Execution routing: <20μs P99
//! - Audit persistence (async): <100μs P99
//! - **Total critical path**: <100μs P99 (excluding async audit)
//!
//! This profiling completes the 30% → 100% performance validation requirement.

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::runtime::Runtime;

// Trading engine components
use chrono::Utc;
use common::{OrderId, OrderSide, OrderStatus};
use rust_decimal::Decimal;
use std::collections::HashMap;
use trading_engine::trading_operations::{
    ExecutionResult, LiquidityFlag, OrderType, TimeInForce, TradingOperations, TradingOrder,
};

/// Helper to create a TradingOrder with all required fields
fn create_order(
    order_type: OrderType,
    side: OrderSide,
    quantity: Decimal,
    price: Decimal,
) -> TradingOrder {
    TradingOrder {
        id: OrderId::new(),
        symbol: "BTCUSD".to_string(),
        order_type,
        side,
        quantity,
        price,
        time_in_force: TimeInForce::GoodTillCancel,
        account_id: Some("benchmark_account".to_string()),
        metadata: HashMap::new(),
        created_at: Utc::now(),
        submitted_at: Some(Utc::now()),
        executed_at: None,
        status: OrderStatus::New,
        fill_quantity: Decimal::ZERO,
        average_fill_price: None,
    }
}

/// Helper to create an ExecutionResult with all required fields
fn create_execution(
    order_id: OrderId,
    quantity: Decimal,
    price: Decimal,
    side: OrderSide,
    liquidity_flag: LiquidityFlag,
) -> ExecutionResult {
    ExecutionResult {
        order_id,
        symbol: "BTCUSD".to_string(),
        side,
        executed_quantity: quantity,
        execution_price: price,
        execution_time: Utc::now(),
        commission: Decimal::new(1, 2), // 0.01
        liquidity_flag,
    }
}

/// Benchmark 1: Order submission latency
fn bench_order_submission(c: &mut Criterion) {
    let mut group = c.benchmark_group("order_submission");
    group.throughput(Throughput::Elements(1));

    let rt = Runtime::new().expect("Failed to create runtime");

    group.bench_function("submit_limit_order", |b| {
        let trading_ops = Arc::new(TradingOperations::new());

        b.to_async(&rt).iter(|| async {
            let order = create_order(
                OrderType::Limit,
                OrderSide::Buy,
                Decimal::new(1, 0),
                Decimal::new(50000, 0),
            );

            let result = trading_ops.submit_order(order).await;
            black_box(result)
        });
    });

    group.bench_function("submit_market_order", |b| {
        let trading_ops = Arc::new(TradingOperations::new());

        b.to_async(&rt).iter(|| async {
            let order = create_order(
                OrderType::Market,
                OrderSide::Sell,
                Decimal::new(1, 0),
                Decimal::ZERO,
            );

            let result = trading_ops.submit_order(order).await;
            black_box(result)
        });
    });

    group.finish();
}

/// Benchmark 2: Execution processing latency
fn bench_execution_processing(c: &mut Criterion) {
    let mut group = c.benchmark_group("execution_processing");
    group.throughput(Throughput::Elements(1));

    let rt = Runtime::new().expect("Failed to create runtime");

    group.bench_function("process_full_fill", |b| {
        let trading_ops = Arc::new(TradingOperations::new());

        b.to_async(&rt).iter(|| async {
            // First submit an order
            let order = create_order(
                OrderType::Limit,
                OrderSide::Buy,
                Decimal::new(1, 0),
                Decimal::new(50000, 0),
            );
            let order_id = order.id;

            let _ = trading_ops
                .submit_order(order)
                .await
                .expect("Failed to submit order");

            // Process execution
            let execution = create_execution(
                order_id,
                Decimal::new(1, 0),
                Decimal::new(50000, 0),
                OrderSide::Buy,
                LiquidityFlag::Maker,
            );

            let result = trading_ops.process_execution(execution).await;
            black_box(result)
        });
    });

    group.bench_function("process_partial_fill", |b| {
        let trading_ops = Arc::new(TradingOperations::new());

        b.to_async(&rt).iter(|| async {
            let order = create_order(
                OrderType::Limit,
                OrderSide::Buy,
                Decimal::new(10, 0),
                Decimal::new(50000, 0),
            );
            let order_id = order.id;

            let _ = trading_ops
                .submit_order(order)
                .await
                .expect("Failed to submit order");

            // Partial fill
            let execution = create_execution(
                order_id,
                Decimal::new(3, 0),
                Decimal::new(50000, 0),
                OrderSide::Buy,
                LiquidityFlag::Taker,
            );

            let result = trading_ops.process_execution(execution).await;
            black_box(result)
        });
    });

    group.finish();
}

/// Benchmark 3: Full trading cycle (critical path)
fn bench_full_trading_cycle(c: &mut Criterion) {
    let mut group = c.benchmark_group("full_trading_cycle");
    group.measurement_time(Duration::from_secs(20));
    group.sample_size(1000);

    let rt = Runtime::new().expect("Failed to create runtime");

    group.bench_function("complete_cycle_limit_order", |b| {
        let trading_ops = Arc::new(TradingOperations::new());

        b.to_async(&rt).iter(|| async {
            let cycle_start = Instant::now();

            // Stage 1: Order creation and submission
            let submission_start = Instant::now();
            let order = create_order(
                OrderType::Limit,
                OrderSide::Buy,
                Decimal::new(1, 0),
                Decimal::new(50000, 0),
            );
            let order_id = order.id;

            let _ = trading_ops
                .submit_order(order)
                .await
                .expect("Failed to submit order");
            let submission_latency = submission_start.elapsed();

            // Stage 2: Execution routing and processing
            let execution_start = Instant::now();
            let execution = create_execution(
                order_id,
                Decimal::new(1, 0),
                Decimal::new(50000, 0),
                OrderSide::Buy,
                LiquidityFlag::Maker,
            );

            trading_ops
                .process_execution(execution)
                .await
                .expect("Failed to process execution");
            let execution_latency = execution_start.elapsed();

            let total_latency = cycle_start.elapsed();

            black_box((submission_latency, execution_latency, total_latency))
        });
    });

    group.bench_function("complete_cycle_market_order", |b| {
        let trading_ops = Arc::new(TradingOperations::new());

        b.to_async(&rt).iter(|| async {
            let cycle_start = Instant::now();

            let order = create_order(
                OrderType::Market,
                OrderSide::Sell,
                Decimal::new(1, 0),
                Decimal::ZERO,
            );
            let order_id = order.id;

            trading_ops
                .submit_order(order)
                .await
                .expect("Failed to submit order");

            let execution = create_execution(
                order_id,
                Decimal::new(1, 0),
                Decimal::new(50000, 0),
                OrderSide::Sell,
                LiquidityFlag::Taker,
            );

            trading_ops
                .process_execution(execution)
                .await
                .expect("Failed to process execution");

            let total_latency = cycle_start.elapsed();
            black_box(total_latency)
        });
    });

    group.finish();
}

/// Benchmark 4: Throughput under load
fn bench_trading_throughput(c: &mut Criterion) {
    let mut group = c.benchmark_group("trading_throughput");

    let rt = Runtime::new().expect("Failed to create runtime");

    for orders_per_batch in &[10, 100, 1000] {
        group.bench_with_input(
            BenchmarkId::new("orders_per_batch", orders_per_batch),
            orders_per_batch,
            |b, &count| {
                let trading_ops = Arc::new(TradingOperations::new());

                b.to_async(&rt).iter(|| async {
                    let start = Instant::now();

                    for i in 0..count {
                        let order = create_order(
                            if i % 2 == 0 {
                                OrderType::Limit
                            } else {
                                OrderType::Market
                            },
                            if i % 2 == 0 {
                                OrderSide::Buy
                            } else {
                                OrderSide::Sell
                            },
                            Decimal::new(1, 0),
                            Decimal::new(50000 + i as i64, 0),
                        );

                        let _ = trading_ops.submit_order(order).await;
                    }

                    black_box(start.elapsed())
                });
            },
        );
    }

    group.finish();
}

criterion_group! {
    name = full_trading_cycle_benchmarks;
    config = Criterion::default()
        .measurement_time(Duration::from_secs(30))
        .sample_size(1000)
        .warm_up_time(Duration::from_secs(5))
        .with_plots();
    targets =
        bench_order_submission,
        bench_execution_processing,
        bench_full_trading_cycle,
        bench_trading_throughput
}

criterion_main!(full_trading_cycle_benchmarks);

/// Validation tests with percentile calculations
#[cfg(test)]
mod performance_validation {
    #[allow(unused_imports)]
    use super::*;

    #[tokio::test]
    async fn validate_full_cycle_latency_targets() {
        println!("\n=== Full Trading Cycle Performance Validation ===\n");

        let trading_ops = Arc::new(TradingOperations::new());
        let iterations = 10000;

        let mut submission_latencies = Vec::new();
        let mut execution_latencies = Vec::new();
        let mut total_latencies = Vec::new();

        for i in 0..iterations {
            let cycle_start = Instant::now();

            // Submit order
            let submission_start = Instant::now();
            let order = create_order(
                OrderType::Limit,
                OrderSide::Buy,
                Decimal::new(1, 0),
                Decimal::new(50000 + i as i64, 0),
            );
            let order_id = order.id;

            trading_ops
                .submit_order(order)
                .await
                .expect("Failed to submit order");
            submission_latencies.push(submission_start.elapsed());

            // Process execution
            let execution_start = Instant::now();
            let execution = create_execution(
                order_id,
                Decimal::new(1, 0),
                Decimal::new(50000, 0),
                OrderSide::Buy,
                LiquidityFlag::Maker,
            );

            trading_ops
                .process_execution(execution)
                .await
                .expect("Failed to process execution");
            execution_latencies.push(execution_start.elapsed());

            total_latencies.push(cycle_start.elapsed());
        }

        // Calculate percentiles inline
        submission_latencies.sort();
        execution_latencies.sort();
        total_latencies.sort();

        let sub_len = submission_latencies.len();
        let sub_p50 = submission_latencies[sub_len / 2];
        let sub_p99 = submission_latencies[(sub_len * 99) / 100];
        let sub_p999 = submission_latencies[(sub_len * 999) / 1000];

        let exec_len = execution_latencies.len();
        let exec_p50 = execution_latencies[exec_len / 2];
        let exec_p99 = execution_latencies[(exec_len * 99) / 100];
        let exec_p999 = execution_latencies[(exec_len * 999) / 1000];

        let total_len = total_latencies.len();
        let total_p50 = total_latencies[total_len / 2];
        let total_p99 = total_latencies[(total_len * 99) / 100];
        let total_p999 = total_latencies[(total_len * 999) / 1000];

        println!("Order Submission Latency:");
        println!("  P50:  {:.1}us", sub_p50.as_micros());
        println!("  P99:  {:.1}us (target: <50us)", sub_p99.as_micros());
        println!("  P999: {:.1}us", sub_p999.as_micros());

        println!("\nExecution Processing Latency:");
        println!("  P50:  {:.1}us", exec_p50.as_micros());
        println!("  P99:  {:.1}us (target: <20us)", exec_p99.as_micros());
        println!("  P999: {:.1}us", exec_p999.as_micros());

        println!("\nTotal Critical Path Latency:");
        println!("  P50:  {:.1}us", total_p50.as_micros());
        println!("  P99:  {:.1}us (target: <100us)", total_p99.as_micros());
        println!("  P999: {:.1}us", total_p999.as_micros());

        // Check performance targets
        let mut violations = Vec::new();
        let sub_p99_us = sub_p99.as_micros() as f64;
        let exec_p99_us = exec_p99.as_micros() as f64;
        let total_p99_us = total_p99.as_micros() as f64;

        if sub_p99_us > 50.0 {
            violations.push(format!(
                "Order submission P99 {:.1}us exceeds 50us target",
                sub_p99_us
            ));
        }
        if exec_p99_us > 20.0 {
            violations.push(format!(
                "Execution routing P99 {:.1}us exceeds 20us target",
                exec_p99_us
            ));
        }
        if total_p99_us > 100.0 {
            violations.push(format!(
                "Total critical path P99 {:.1}us exceeds 100us target",
                total_p99_us
            ));
        }

        if !violations.is_empty() {
            println!("\nPerformance Target Violations:");
            for violation in &violations {
                println!("  - {}", violation);
            }
        } else {
            println!("\nAll HFT performance targets met!");
        }

        println!("\n=== Performance Validation Complete ===\n");

        // Assertions
        assert!(
            sub_p99.as_micros() < 50,
            "Order submission P99 exceeds 50us: {}us",
            sub_p99.as_micros()
        );

        assert!(
            exec_p99.as_micros() < 20,
            "Execution processing P99 exceeds 20us: {}us",
            exec_p99.as_micros()
        );

        assert!(
            total_p99.as_micros() < 100,
            "Total critical path P99 exceeds 100us: {}us",
            total_p99.as_micros()
        );
    }

    #[tokio::test]
    async fn validate_throughput_capacity() {
        println!("\n=== Throughput Capacity Validation ===\n");

        let trading_ops = Arc::new(TradingOperations::new());
        let total_orders = 100000;

        let start = Instant::now();
        for i in 0..total_orders {
            let order = create_order(
                OrderType::Limit,
                if i % 2 == 0 {
                    OrderSide::Buy
                } else {
                    OrderSide::Sell
                },
                Decimal::new(1, 0),
                Decimal::new(50000 + (i % 100) as i64, 0),
            );

            let _ = trading_ops.submit_order(order).await;
        }

        let elapsed = start.elapsed();
        let orders_per_sec = (total_orders as f64 / elapsed.as_secs_f64()) as u64;

        println!("Total orders processed: {}", total_orders);
        println!("Total time: {:?}", elapsed);
        println!("Throughput: {} orders/sec", orders_per_sec);
        println!("\n=== Throughput Validation Complete ===\n");

        // HFT systems should handle >10K orders/sec
        assert!(
            orders_per_sec >= 10000,
            "Throughput too low: {} orders/sec (target: >10K)",
            orders_per_sec
        );
    }
}
