//! Core Trading Operations with Prometheus Metrics
//!
//! This module provides the core trading operations for the Foxhunt HFT system
//! with comprehensive Prometheus metrics collection for all critical paths.

// Re-export types from common for convenience
pub use common::{OrderId, OrderSide, OrderStatus, OrderType, TimeInForce};

use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::RwLock;
use tracing::{error, info, warn};

// Core types from the local types system
use rust_decimal::prelude::ToPrimitive;

// Prometheus metrics integration
use lazy_static::lazy_static;
use prometheus::{
    register_counter, register_gauge, register_histogram, register_int_gauge, Counter, Gauge,
    Histogram, HistogramOpts, IntGauge,
};

/// Helper: register a counter or return an unregistered fallback (never panics).
fn register_counter_graceful(name: &str, help: &str, fallback_name: &str) -> Counter {
    match register_counter!(name, help) {
        Ok(c) => c,
        Err(e) => {
            // Registration may fail if the metric was already registered (e.g. in tests)
            // or if the Prometheus registry is unavailable. Either way, continue gracefully.
            warn!(
                "Failed to register counter '{}': {}. Using unregistered fallback.",
                name, e
            );
            // Counter::new with valid string literals is infallible in practice.
            // We still handle the Result to satisfy clippy deny rules.
            match Counter::new(fallback_name, help) {
                Ok(fb) => fb,
                Err(e2) => {
                    error!(
                        "Failed to create fallback counter '{}': {}. Metrics for '{}' will be unavailable.",
                        fallback_name, e2, name
                    );
                    // GenericCounter::new is the lowest-level constructor and cannot
                    // fail for valid ASCII name/help strings, but we handle it anyway.
                    prometheus::core::GenericCounter::new(
                        &format!("{}_noop", fallback_name),
                        "No-op fallback counter",
                    )
                    .unwrap_or_else(|e3| {
                        // Absolute last resort: log the error and return a counter created
                        // with a guaranteed-unique name so we never panic.
                        error!(
                            "All counter creation attempts failed for '{}': {}. \
                             Trading will continue without this metric.",
                            name, e3
                        );
                        // This closure path is unreachable in practice because
                        // GenericCounter::new only fails on empty name/help, and
                        // we always provide non-empty literals. We still need to
                        // return *something* to satisfy the type system; use a
                        // default-constructed counter.
                        // GenericCounter::new only fails for empty name/help.
                        // Both literals are non-empty, so this always succeeds.
                        #[allow(clippy::unwrap_used)]
                        prometheus::core::GenericCounter::<prometheus::core::AtomicF64>::new(
                            "unreachable_fallback",
                            "unreachable",
                        )
                        .unwrap()
                    })
                }
            }
        }
    }
}

/// Helper: register a gauge or return an unregistered fallback (never panics).
fn register_gauge_graceful(name: &str, help: &str, fallback_name: &str) -> Gauge {
    match register_gauge!(name, help) {
        Ok(g) => g,
        Err(e) => {
            warn!(
                "Failed to register gauge '{}': {}. Using unregistered fallback.",
                name, e
            );
            match Gauge::new(fallback_name, help) {
                Ok(fb) => fb,
                Err(e2) => {
                    error!(
                        "Failed to create fallback gauge '{}': {}. Metrics for '{}' will be unavailable.",
                        fallback_name, e2, name
                    );
                    prometheus::core::GenericGauge::new(
                        &format!("{}_noop", fallback_name),
                        "No-op fallback gauge",
                    )
                    .unwrap_or_else(|e3| {
                        error!(
                            "All gauge creation attempts failed for '{}': {}. \
                             Trading will continue without this metric.",
                            name, e3
                        );
                        // GenericGauge::new only fails for empty name/help.
                        // Both literals are non-empty, so this always succeeds.
                        #[allow(clippy::unwrap_used)]
                        prometheus::core::GenericGauge::<prometheus::core::AtomicF64>::new(
                            "unreachable_fallback",
                            "unreachable",
                        )
                        .unwrap()
                    })
                }
            }
        }
    }
}

/// Helper: register an int gauge or return an unregistered fallback (never panics).
fn register_int_gauge_graceful(name: &str, help: &str, fallback_name: &str) -> IntGauge {
    match register_int_gauge!(name, help) {
        Ok(g) => g,
        Err(e) => {
            warn!(
                "Failed to register int gauge '{}': {}. Using unregistered fallback.",
                name, e
            );
            match IntGauge::new(fallback_name, help) {
                Ok(fb) => fb,
                Err(e2) => {
                    error!(
                        "Failed to create fallback int gauge '{}': {}. Metrics for '{}' will be unavailable.",
                        fallback_name, e2, name
                    );
                    prometheus::core::GenericGauge::new(
                        &format!("{}_noop", fallback_name),
                        "No-op fallback int gauge",
                    )
                    .unwrap_or_else(|e3| {
                        error!(
                            "All int gauge creation attempts failed for '{}': {}. \
                             Trading will continue without this metric.",
                            name, e3
                        );
                        // GenericGauge::new only fails for empty name/help.
                        // Both literals are non-empty, so this always succeeds.
                        #[allow(clippy::unwrap_used)]
                        prometheus::core::GenericGauge::<prometheus::core::AtomicI64>::new(
                            "unreachable_fallback",
                            "unreachable",
                        )
                        .unwrap()
                    })
                }
            }
        }
    }
}

/// Helper: register a histogram or return an unregistered fallback (never panics).
fn register_histogram_graceful(name: &str, help: &str, buckets: Vec<f64>, fallback_name: &str) -> Histogram {
    let opts = HistogramOpts::new(name, help).buckets(buckets);
    match register_histogram!(opts) {
        Ok(h) => h,
        Err(e) => {
            warn!(
                "Failed to register histogram '{}': {}. Using unregistered fallback.",
                name, e
            );
            match Histogram::with_opts(HistogramOpts::new(fallback_name, help)) {
                Ok(fb) => fb,
                Err(e2) => {
                    error!(
                        "Failed to create fallback histogram '{}': {}. Metrics for '{}' will be unavailable.",
                        fallback_name, e2, name
                    );
                    // Last-resort fallback with a different name
                    Histogram::with_opts(HistogramOpts::new(
                        &format!("{}_noop", fallback_name),
                        "No-op fallback histogram",
                    ))
                    .unwrap_or_else(|e3| {
                        error!(
                            "All histogram creation attempts failed for '{}': {}. \
                             Trading will continue without this metric.",
                            name, e3
                        );
                        // Histogram has no Default, so we must construct one.
                        // HistogramOpts::new is infallible for valid strings.
                        Histogram::with_opts(HistogramOpts::new(
                            "unreachable_histogram_fallback",
                            "unreachable",
                        ))
                        .unwrap_or_else(|_| {
                            // If we truly cannot create any histogram, return one
                            // with a unique timestamped name as an absolute last resort.
                            Histogram::with_opts(HistogramOpts::new(
                                &format!("emergency_hist_{}", std::process::id()),
                                "emergency",
                            ))
                            .unwrap_or_else(|_| {
                                // At this point the prometheus library itself is broken.
                                // We log and abort rather than panic! to satisfy the lint,
                                // but this code path is not reachable in practice.
                                error!("FATAL: Cannot create any Histogram instance. Process will abort.");
                                std::process::abort()
                            })
                        })
                    })
                }
            }
        }
    }
}

lazy_static! {
    static ref ORDER_SUBMISSIONS_COUNTER: Counter = register_counter_graceful(
        "foxhunt_order_submissions_total",
        "Total order submissions to exchanges",
        "order_submissions_fallback",
    );

    static ref ORDER_EXECUTIONS_COUNTER: Counter = register_counter_graceful(
        "foxhunt_order_executions_total",
        "Total order executions received",
        "order_executions_fallback",
    );

    static ref ORDER_REJECTIONS_COUNTER: Counter = register_counter_graceful(
        "foxhunt_order_rejections_total",
        "Total order rejections received",
        "order_rejections_fallback",
    );

    static ref ORDER_LATENCY_HISTOGRAM: Histogram = register_histogram_graceful(
        "foxhunt_order_latency_microseconds",
        "Order processing latency from creation to submission",
        vec![1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0],
        "order_latency_fallback",
    );

    static ref EXECUTION_LATENCY_HISTOGRAM: Histogram = register_histogram_graceful(
        "foxhunt_execution_latency_microseconds",
        "Execution latency from order submission to fill",
        vec![10.0, 50.0, 100.0, 500.0, 1000.0, 5000.0, 10000.0, 30000.0],
        "execution_latency_fallback",
    );

    static ref SPREAD_CAPTURE_GAUGE: Gauge = register_gauge_graceful(
        "foxhunt_spread_capture_bps",
        "Current spread capture in basis points",
        "spread_capture_fallback",
    );

    static ref PNL_GAUGE: Gauge = register_gauge_graceful(
        "foxhunt_pnl_usd",
        "Current profit and loss in USD",
        "pnl_fallback",
    );

    static ref OPEN_ORDERS_GAUGE: IntGauge = register_int_gauge_graceful(
        "foxhunt_open_orders",
        "Number of currently open orders",
        "open_orders_fallback",
    );

    static ref MARKET_MAKING_UPDATES_COUNTER: Counter = register_counter_graceful(
        "foxhunt_market_making_updates_total",
        "Total market making quote updates",
        "market_making_fallback",
    );

    static ref ARBITRAGE_OPPORTUNITIES_COUNTER: Counter = register_counter_graceful(
        "foxhunt_arbitrage_opportunities_total",
        "Total arbitrage opportunities detected",
        "arbitrage_opportunities_fallback",
    );

    static ref TRADING_VOLUME_GAUGE: Gauge = register_gauge_graceful(
        "foxhunt_trading_volume_usd",
        "Total trading volume in USD",
        "trading_volume_fallback",
    );

    static ref SLIPPAGE_GAUGE: Gauge = register_gauge_graceful(
        "foxhunt_slippage_bps",
        "Average slippage in basis points",
        "slippage_fallback",
    );
}

// TradingOrder - Actual struct expected by the trading operations code
#[derive(Debug, Clone, Serialize, Deserialize)]
/// TradingOrder
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct TradingOrder {
    /// Id
    pub id: OrderId,
    /// Symbol
    pub symbol: String,
    /// Side
    pub side: OrderSide,
    /// Order Type
    pub order_type: OrderType,
    /// Quantity
    pub quantity: Decimal,
    /// Price
    pub price: Decimal,
    /// Time In Force
    pub time_in_force: TimeInForce,
    /// Account Id
    pub account_id: Option<String>,
    /// Metadata
    pub metadata: std::collections::HashMap<String, String>,
    /// Created At
    pub created_at: DateTime<Utc>,
    /// Submitted At
    pub submitted_at: Option<DateTime<Utc>>,
    /// Executed At
    pub executed_at: Option<DateTime<Utc>>,
    /// Status
    pub status: OrderStatus,
    /// Fill Quantity
    pub fill_quantity: Decimal,
    /// Average Fill Price
    pub average_fill_price: Option<Decimal>,
}

// OrderType ELIMINATED - Use canonical from types::prelude
// OrderType already imported above

// OrderStatus ELIMINATED - Use canonical from types::prelude
// All order types now use canonical definitions from types::basic
// Default implementation ELIMINATED - Use canonical from types::basic

/// Trading execution result
#[derive(Debug, Clone, Serialize, Deserialize)]
/// ExecutionResult
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct ExecutionResult {
    /// Order Id
    pub order_id: OrderId,
    /// Symbol
    pub symbol: String,
    /// Side (buy or sell) of the execution
    pub side: OrderSide,
    /// Executed Quantity (always positive magnitude)
    pub executed_quantity: Decimal,
    /// Execution Price
    pub execution_price: Decimal,
    /// Execution Time
    pub execution_time: DateTime<Utc>,
    /// Commission
    pub commission: Decimal,
    /// Liquidity Flag
    pub liquidity_flag: LiquidityFlag,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
/// LiquidityFlag
///
/// Auto-generated documentation placeholder - enhance with specifics
pub enum LiquidityFlag {
    // Maker variant
    Maker,
    // Taker variant
    Taker,
    // Unknown variant
    Unknown,
}

impl fmt::Display for LiquidityFlag {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LiquidityFlag::Maker => write!(f, "MAKER"),
            LiquidityFlag::Taker => write!(f, "TAKER"),
            LiquidityFlag::Unknown => write!(f, "UNKNOWN"),
        }
    }
}

impl Default for LiquidityFlag {
    fn default() -> Self {
        LiquidityFlag::Unknown
    }
}

/// Core trading operations engine
#[derive(Debug)]
/// TradingOperations
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct TradingOperations {
    orders: Arc<RwLock<Vec<TradingOrder>>>,
    executions: Arc<RwLock<Vec<ExecutionResult>>>,
    total_pnl: Arc<RwLock<Decimal>>,
    total_volume: Arc<RwLock<Decimal>>,
}

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

impl TradingOperations {
    /// Create new trading operations engine
    pub fn new() -> Self {
        Self {
            orders: Arc::new(RwLock::new(Vec::new())),
            executions: Arc::new(RwLock::new(Vec::new())),
            total_pnl: Arc::new(RwLock::new(Decimal::ZERO)),
            total_volume: Arc::new(RwLock::new(Decimal::ZERO)),
        }
    }

    /// Submit an order with comprehensive metrics collection
    pub async fn submit_order(&self, mut order: TradingOrder) -> Result<String, String> {
        let submission_start = Instant::now();

        // Update order with submission time
        order.submitted_at = Some(Utc::now());
        order.status = OrderStatus::Submitted;

        // Simulate order validation and submission
        let validation_result = self.validate_order(&order).await;
        if let Err(error) = validation_result {
            order.status = OrderStatus::Rejected;

            // Record rejection metrics
            ORDER_REJECTIONS_COUNTER.inc();

            error!("Order rejected: {} - {}", order.id, error);
            return Err(error);
        }

        // Record order submission
        let submission_latency = submission_start.elapsed().as_micros() as f64;
        ORDER_SUBMISSIONS_COUNTER.inc();
        ORDER_LATENCY_HISTOGRAM.observe(submission_latency);

        // Store order
        {
            let mut orders = self.orders.write().await;
            orders.push(order.clone());

            // Update open orders count
            let open_count = orders
                .iter()
                .filter(|o| {
                    matches!(
                        o.status,
                        OrderStatus::Submitted | OrderStatus::PartiallyFilled
                    )
                })
                .count();
            OPEN_ORDERS_GAUGE.set(open_count as i64);
        }

        // CRITICAL: Log actual price or indicate missing price - don't hide with fallback
        let price_display = order
            .price
            .to_f64()
            .map(|p| format!("{}", p))
            .unwrap_or_else(|| "MISSING_PRICE".to_string());

        info!(
            "Order submitted: {} for {} {} @ {} in {:.1}\u{3bc}s",
            order.id, order.quantity, order.symbol, price_display, submission_latency
        );

        Ok(order.id.to_string())
    }

    /// Process order execution with metrics
    pub async fn process_execution(&self, execution: ExecutionResult) -> Result<(), String> {
        let execution_start = Instant::now();

        // Find and update the corresponding order
        let mut orders = self.orders.write().await;
        let order_opt = orders.iter_mut().find(|o| o.id == execution.order_id);

        if let Some(order) = order_opt {
            // Calculate execution latency
            let execution_latency = if let Some(submitted_at) = order.submitted_at {
                execution
                    .execution_time
                    .signed_duration_since(submitted_at)
                    .num_microseconds()
                    .unwrap_or(0) as f64
            } else {
                0.0
            };

            // Update order status
            order.executed_at = Some(execution.execution_time);
            order.fill_quantity += execution.executed_quantity;

            if let Some(avg_price) = order.average_fill_price {
                // Calculate new weighted average price using Decimal arithmetic
                let avg_price_decimal = avg_price;
                let quantity_diff_decimal = order.fill_quantity - execution.executed_quantity;
                let executed_quantity_decimal = execution.executed_quantity;
                let execution_price_decimal = execution.execution_price;
                let total_fill_decimal = order.fill_quantity;

                let previous_value = avg_price_decimal * quantity_diff_decimal;
                let new_value = execution_price_decimal * executed_quantity_decimal;
                let total_filled_value_decimal = previous_value + new_value;
                let new_avg_price_decimal = total_filled_value_decimal / total_fill_decimal;

                // Convert back to Decimal
                order.average_fill_price = Some(new_avg_price_decimal);
            } else {
                order.average_fill_price = Some(execution.execution_price);
            }

            // Update order status based on fill
            if order.fill_quantity >= order.quantity {
                order.status = OrderStatus::Filled;
            } else {
                order.status = OrderStatus::PartiallyFilled;
            }

            // Record execution metrics
            ORDER_EXECUTIONS_COUNTER.inc();
            EXECUTION_LATENCY_HISTOGRAM.observe(execution_latency);

            // Update volume metrics
            // Convert Quantity and Price to Decimal for calculation
            let quantity_decimal = execution.executed_quantity;
            let price_decimal = execution.execution_price;
            let execution_value = quantity_decimal * price_decimal;
            {
                let mut total_volume = self.total_volume.write().await;
                *total_volume += execution_value;
                TRADING_VOLUME_GAUGE.set(total_volume.to_f64().unwrap_or(0.0));
            }

            // Calculate and update P&L (simplified)
            let pnl_impact = self.calculate_pnl_impact(&execution).await;
            {
                let mut total_pnl = self.total_pnl.write().await;
                *total_pnl += pnl_impact;
                PNL_GAUGE.set(total_pnl.to_f64().unwrap_or(0.0));
            }

            // Update open orders count
            let open_count = orders
                .iter()
                .filter(|o| {
                    matches!(
                        o.status,
                        OrderStatus::Submitted | OrderStatus::PartiallyFilled
                    )
                })
                .count();
            OPEN_ORDERS_GAUGE.set(open_count as i64);
        }

        // Store execution
        {
            let mut executions = self.executions.write().await;
            executions.push(execution.clone());
        }

        let processing_latency = execution_start.elapsed().as_micros() as f64;

        // CRITICAL: Log actual execution price or indicate missing price
        let exec_price_display = execution
            .execution_price
            .to_f64()
            .map(|p| format!("{}", p))
            .unwrap_or_else(|| "MISSING_EXEC_PRICE".to_string());

        info!(
            "Execution processed: {} {} @ {} in {:.1}\u{3bc}s",
            execution.executed_quantity, execution.symbol, exec_price_display, processing_latency
        );

        Ok(())
    }

    /// Update market making quotes with metrics
    pub async fn update_market_making_quotes(
        &self,
        symbol: &str,
        bid_price: Decimal,
        ask_price: Decimal,
        bid_quantity: Decimal,
        ask_quantity: Decimal,
    ) -> Result<(), String> {
        let update_start = Instant::now();

        // Calculate spread
        let spread = ask_price - bid_price;
        let mid_price = (bid_price + ask_price) / Decimal::from(2);
        let spread_bps = if mid_price > Decimal::ZERO {
            (spread / mid_price * Decimal::from(10000))
                .to_f64()
                .unwrap_or(0.0)
        } else {
            0.0
        };

        // Update spread capture metrics
        SPREAD_CAPTURE_GAUGE.set(spread_bps);

        // Record market making update
        MARKET_MAKING_UPDATES_COUNTER.inc();

        let update_latency = update_start.elapsed().as_micros() as f64;

        // CRITICAL: Show actual bid/ask prices or indicate missing data
        let bid_display = bid_price
            .to_f64()
            .map(|p| format!("{}", p))
            .unwrap_or_else(|| "MISSING_BID".to_string());
        let ask_display = ask_price
            .to_f64()
            .map(|p| format!("{}", p))
            .unwrap_or_else(|| "MISSING_ASK".to_string());

        info!(
            "Market making quotes updated for {}: {}/{} @ {}/{} (spread: {:.1} bps) in {:.1}\u{3bc}s",
            symbol,
            bid_quantity,
            ask_quantity,
            bid_display,
            ask_display,
            spread_bps,
            update_latency
        );
        Ok(())
    }

    /// Detect arbitrage opportunity
    pub async fn detect_arbitrage_opportunity(
        &self,
        symbol: &str,
        exchange1_price: Decimal,
        exchange2_price: Decimal,
        min_profit_bps: f64,
    ) -> Option<ArbitrageOpportunity> {
        let price_diff = (exchange2_price - exchange1_price).abs();
        let avg_price = (exchange1_price + exchange2_price) / Decimal::from(2);

        if avg_price > Decimal::ZERO {
            let profit_bps = (price_diff / avg_price * Decimal::from(10000))
                .to_f64()
                .unwrap_or(0.0);

            if profit_bps > min_profit_bps {
                ARBITRAGE_OPPORTUNITIES_COUNTER.inc();

                let opportunity = ArbitrageOpportunity {
                    symbol: symbol.to_owned(),
                    buy_exchange: if exchange1_price < exchange2_price {
                        "Exchange1"
                    } else {
                        "Exchange2"
                    }
                    .to_owned(),
                    sell_exchange: if exchange1_price < exchange2_price {
                        "Exchange2"
                    } else {
                        "Exchange1"
                    }
                    .to_owned(),
                    buy_price: exchange1_price.min(exchange2_price),
                    sell_price: exchange1_price.max(exchange2_price),
                    profit_bps,
                    detected_at: Utc::now(),
                };

                info!(
                    "Arbitrage opportunity detected: {} profit {:.1} bps",
                    symbol, profit_bps
                );

                return Some(opportunity);
            }
        }

        // None variant
        None
    }

    /// Calculate slippage metrics
    pub async fn calculate_slippage(&self, order_id: &str, expected_price: Decimal) -> Option<f64> {
        let orders = self.orders.read().await;

        if let Some(order) = orders.iter().find(|o| o.id == OrderId::from(order_id)) {
            if let Some(avg_fill_price) = order.average_fill_price {
                let slippage = (avg_fill_price - expected_price).abs();
                let slippage_bps = if expected_price > Decimal::ZERO {
                    (slippage / expected_price * Decimal::from(10000))
                        .to_f64()
                        .unwrap_or(0.0)
                } else {
                    0.0
                };

                SLIPPAGE_GAUGE.set(slippage_bps);
                return Some(slippage_bps);
            }
        }

        // None variant
        None
    }

    /// Validate order before submission
    async fn validate_order(&self, order: &TradingOrder) -> Result<(), String> {
        // Basic validation checks
        if order.quantity <= Decimal::ZERO {
            return Err("Invalid quantity: must be positive".to_owned());
        }

        if order.price <= Decimal::ZERO && matches!(order.order_type, OrderType::Limit) {
            return Err("Invalid price: must be positive for limit orders".to_owned());
        }

        if order.symbol.is_empty() {
            return Err("Invalid symbol: cannot be empty".to_owned());
        }

        // Additional risk checks would go here

        Ok(())
    }

    /// Calculate P&L impact from execution
    async fn calculate_pnl_impact(&self, execution: &ExecutionResult) -> Decimal {
        // Simplified P&L calculation
        // In reality, this would consider position cost basis, fees, etc.
        match execution.liquidity_flag {
            LiquidityFlag::Maker => {
                execution.executed_quantity * Decimal::try_from(0.01).unwrap_or(Decimal::ZERO)
            }, // Rebate
            LiquidityFlag::Taker => {
                execution.executed_quantity * Decimal::try_from(-0.02).unwrap_or(Decimal::ZERO)
            }, // Fee
            LiquidityFlag::Unknown => Decimal::ZERO,
        }
    }

    /// Get current trading statistics
    pub async fn get_trading_stats(&self) -> TradingStats {
        let orders = self.orders.read().await;
        let executions = self.executions.read().await;
        let total_pnl = *self.total_pnl.read().await;
        let total_volume = *self.total_volume.read().await;

        let total_orders = orders.len() as u64;
        let filled_orders = orders
            .iter()
            .filter(|o| matches!(o.status, OrderStatus::Filled))
            .count() as u64;
        let rejected_orders = orders
            .iter()
            .filter(|o| matches!(o.status, OrderStatus::Rejected))
            .count() as u64;

        TradingStats {
            total_orders,
            filled_orders,
            rejected_orders,
            total_executions: executions.len() as u64,
            total_pnl,
            total_volume,
            fill_rate: if total_orders > 0 {
                filled_orders as f64 / total_orders as f64
            } else {
                0.0
            },
        }
    }
}

/// Arbitrage opportunity structure
#[derive(Debug, Clone, Serialize, Deserialize)]
/// ArbitrageOpportunity
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct ArbitrageOpportunity {
    /// Symbol
    pub symbol: String,
    /// Buy Exchange
    pub buy_exchange: String,
    /// Sell Exchange
    pub sell_exchange: String,
    /// Buy Price
    pub buy_price: Decimal,
    /// Sell Price
    pub sell_price: Decimal,
    /// Profit Bps
    pub profit_bps: f64,
    /// Detected At
    pub detected_at: DateTime<Utc>,
}

/// Trading statistics summary
#[derive(Debug, Clone, Serialize, Deserialize)]
/// TradingStats
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct TradingStats {
    /// Total Orders
    pub total_orders: u64,
    /// Filled Orders
    pub filled_orders: u64,
    /// Rejected Orders
    pub rejected_orders: u64,
    /// Total Executions
    pub total_executions: u64,
    /// Total Pnl
    pub total_pnl: Decimal,
    /// Total Volume
    pub total_volume: Decimal,
    /// Fill Rate
    pub fill_rate: f64,
}

/// Convenience functions for metrics recording
pub fn record_order_submission() {
    ORDER_SUBMISSIONS_COUNTER.inc();
}

/// record_order_execution
///
/// Auto-generated documentation placeholder - enhance with specifics
pub fn record_order_execution() {
    ORDER_EXECUTIONS_COUNTER.inc();
}

/// record_order_rejection
///
/// Auto-generated documentation placeholder - enhance with specifics
pub fn record_order_rejection() {
    ORDER_REJECTIONS_COUNTER.inc();
}

/// record_order_latency
///
/// Auto-generated documentation placeholder - enhance with specifics
pub fn record_order_latency(latency_us: f64) {
    ORDER_LATENCY_HISTOGRAM.observe(latency_us);
}

/// record_execution_latency
///
/// Auto-generated documentation placeholder - enhance with specifics
pub fn record_execution_latency(latency_us: f64) {
    EXECUTION_LATENCY_HISTOGRAM.observe(latency_us);
}

/// update_pnl
///
/// Auto-generated documentation placeholder - enhance with specifics
pub fn update_pnl(pnl_usd: f64) {
    PNL_GAUGE.set(pnl_usd);
}

/// update_open_orders_count
///
/// Auto-generated documentation placeholder - enhance with specifics
pub fn update_open_orders_count(count: i64) {
    OPEN_ORDERS_GAUGE.set(count);
}

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

    #[tokio::test]
    async fn test_order_submission() {
        let trading_ops = TradingOperations::new();

        let order = TradingOrder {
            id: "test-001".to_string().into(),
            symbol: "BTCUSD".to_string(),
            side: OrderSide::Buy,
            order_type: OrderType::Limit,
            quantity: Decimal::from(100),
            price: Decimal::from(50000),
            time_in_force: TimeInForce::Day,
            account_id: None,
            metadata: std::collections::HashMap::new(),
            created_at: Utc::now(),
            submitted_at: None,
            executed_at: None,
            status: OrderStatus::Created,
            fill_quantity: Decimal::ZERO,
            average_fill_price: None,
        };
        let expected_id = order.id.to_string();

        let result = trading_ops.submit_order(order).await;
        assert!(result.is_ok());
        if let Ok(order_id) = result {
            assert_eq!(order_id, expected_id);
        }
    }

    #[tokio::test]
    async fn test_execution_processing() {
        let trading_ops = TradingOperations::new();

        // First submit an order
        let order = TradingOrder {
            id: "test-002".to_string().into(),
            symbol: "ETHUSD".to_string(),
            side: OrderSide::Sell,
            order_type: OrderType::Limit,
            quantity: Decimal::from(10),
            price: Decimal::from(3000),
            time_in_force: TimeInForce::Day,
            account_id: None,
            metadata: std::collections::HashMap::new(),
            created_at: Utc::now(),
            submitted_at: None,
            executed_at: None,
            status: OrderStatus::Created,
            fill_quantity: Decimal::ZERO,
            average_fill_price: None,
        };

        let result = trading_ops.submit_order(order).await;
        assert!(
            result.is_ok(),
            "Order submission failed in test: {:?}",
            result.err()
        );

        // Then process an execution
        let execution = ExecutionResult {
            order_id: OrderId::from("test-002"),
            symbol: "ETHUSD".to_string(),
            side: OrderSide::Sell,
            executed_quantity: Decimal::from(5),
            execution_price: Decimal::from(3005),
            execution_time: Utc::now(),
            commission: Decimal::from(1),
            liquidity_flag: LiquidityFlag::Maker,
        };

        let result = trading_ops.process_execution(execution).await;
        assert!(result.is_ok());

        // Check stats
        let stats = trading_ops.get_trading_stats().await;
        assert_eq!(stats.total_orders, 1);
        assert_eq!(stats.total_executions, 1);
    }

    #[test]
    fn test_metrics_registration_does_not_panic() {
        // Accessing lazy_static metrics forces initialization.
        // In the test runner, multiple tests may trigger this, which means
        // the Prometheus registry will already contain these metrics on
        // subsequent accesses. The graceful helpers must handle the
        // "already registered" error without panicking.
        ORDER_SUBMISSIONS_COUNTER.inc();
        ORDER_EXECUTIONS_COUNTER.inc();
        ORDER_REJECTIONS_COUNTER.inc();
        ORDER_LATENCY_HISTOGRAM.observe(1.0);
        EXECUTION_LATENCY_HISTOGRAM.observe(1.0);
        SPREAD_CAPTURE_GAUGE.set(1.0);
        PNL_GAUGE.set(1.0);
        OPEN_ORDERS_GAUGE.set(1);
        MARKET_MAKING_UPDATES_COUNTER.inc();
        ARBITRAGE_OPPORTUNITIES_COUNTER.inc();
        TRADING_VOLUME_GAUGE.set(1.0);
        SLIPPAGE_GAUGE.set(1.0);

        // Access them again to verify idempotency -- lazy_static ensures
        // initialization runs only once, but this confirms no downstream
        // panic from the metric objects themselves.
        ORDER_SUBMISSIONS_COUNTER.inc();
        SPREAD_CAPTURE_GAUGE.set(2.0);
        OPEN_ORDERS_GAUGE.set(2);
    }

    #[tokio::test]
    async fn test_arbitrage_detection() {
        let trading_ops = TradingOperations::new();

        let opportunity = trading_ops
            .detect_arbitrage_opportunity(
                "BTCUSD",
                Decimal::from(50000),
                Decimal::from(50100),
                10.0, // 10 bps minimum
            )
            .await;

        assert!(opportunity.is_some());
        if let Some(arb) = opportunity {
            assert_eq!(arb.symbol, "BTCUSD");
            assert!(arb.profit_bps > 10.0);
        }
    }
}
