//! # Foxhunt HFT Trading System - Production Metrics
//!
//! Comprehensive Prometheus metrics for high-frequency trading system monitoring.
//! Includes business metrics, performance metrics, and system health indicators.

// Allow panic/expect/eprintln for metrics initialization - these are intentional
// for CATASTROPHIC failures and logging in metrics subsystem
#![allow(clippy::print_stderr)] // eprintln used in CATASTROPHIC abort paths
#![allow(clippy::expect_used)]
#![allow(clippy::missing_docs_in_private_items)]

use once_cell::sync::Lazy;
use prometheus::{
    GaugeVec, Histogram, HistogramOpts, HistogramVec, IntCounterVec, IntGaugeVec, Opts, Registry,
    Result as PrometheusResult,
};
use std::sync::Arc;
use std::time::{Duration, Instant};

// Using simple tracing instead of OpenTelemetry for HFT performance
// OpenTelemetry was removed - too heavy for HFT requirements
use lru::LruCache;
use parking_lot::RwLock;
use std::num::NonZeroUsize;

// Import cardinality limiter for metrics optimization
use super::cardinality_limiter::bucket_instrument;

// ============================================================================
// NO-OP METRIC HELPERS - Prevent panics on fallback failure
// ============================================================================
//
// These static no-op metrics are created once at startup and reused whenever
// a metric creation fails. They provide graceful degradation instead of panics.

/// Global no-op IntCounterVec for fallback use
static NOOP_INT_COUNTER: Lazy<IntCounterVec> = Lazy::new(|| {
    IntCounterVec::new(Opts::new("foxhunt_noop_counter", "No-op counter"), &[])
        .or_else(|_| IntCounterVec::new(Opts::new("_noop", ""), &[]))
        .unwrap_or_else(|e| {
            eprintln!("CATASTROPHIC: Cannot create no-op metric counter: {e}. Aborting.");
            std::process::abort()
        })
});

/// Global no-op HistogramVec for fallback use
static NOOP_HISTOGRAM: Lazy<HistogramVec> = Lazy::new(|| {
    HistogramVec::new(
        HistogramOpts::new("foxhunt_noop_histogram", "No-op histogram"),
        &[],
    )
    .or_else(|_| HistogramVec::new(HistogramOpts::new("_noop", ""), &[]))
    .unwrap_or_else(|e| {
        eprintln!("CATASTROPHIC: Cannot create no-op histogram: {e}. Aborting.");
        std::process::abort()
    })
});

/// Global no-op GaugeVec for fallback use
static NOOP_GAUGE: Lazy<GaugeVec> = Lazy::new(|| {
    GaugeVec::new(Opts::new("foxhunt_noop_gauge", "No-op gauge"), &[])
        .or_else(|_| GaugeVec::new(Opts::new("_noop", ""), &[]))
        .unwrap_or_else(|e| {
            eprintln!("CATASTROPHIC: Cannot create no-op gauge: {e}. Aborting.");
            std::process::abort()
        })
});

/// Global no-op IntGaugeVec for fallback use
static NOOP_INT_GAUGE: Lazy<IntGaugeVec> = Lazy::new(|| {
    IntGaugeVec::new(Opts::new("foxhunt_noop_int_gauge", "No-op int gauge"), &[])
        .or_else(|_| IntGaugeVec::new(Opts::new("_noop", ""), &[]))
        .unwrap_or_else(|e| {
            eprintln!("CATASTROPHIC: Cannot create no-op int gauge: {e}. Aborting.");
            std::process::abort()
        })
});

/// Return a clone of the global no-op IntCounterVec
///
/// Used as final fallback when metric creation fails. Returns a static
/// no-op metric that was created once at startup. Never panics in production.
fn create_noop_int_counter_vec() -> IntCounterVec {
    NOOP_INT_COUNTER.clone()
}

/// Return a clone of the global no-op HistogramVec
///
/// Used as final fallback when metric creation fails. Returns a static
/// no-op metric that was created once at startup. Never panics in production.
fn create_noop_histogram_vec() -> HistogramVec {
    NOOP_HISTOGRAM.clone()
}

/// Return a clone of the global no-op GaugeVec
///
/// Used as final fallback when metric creation fails. Returns a static
/// no-op metric that was created once at startup. Never panics in production.
fn create_noop_gauge_vec() -> GaugeVec {
    NOOP_GAUGE.clone()
}

/// Return a clone of the global no-op IntGaugeVec
///
/// Used as final fallback when metric creation fails. Returns a static
/// no-op metric that was created once at startup. Never panics in production.
fn create_noop_int_gauge_vec() -> IntGaugeVec {
    NOOP_INT_GAUGE.clone()
}

/// Global metrics registry for the Foxhunt trading system
pub static METRICS_REGISTRY: Lazy<Registry> = Lazy::new(|| {
    Registry::new_custom(
        Some("foxhunt".to_owned()),
        Some(
            vec![
                ("environment".to_owned(), "production".to_owned()),
                ("system".to_owned(), "hft-trading".to_owned()),
            ]
            .into_iter()
            .collect(),
        ),
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create metrics registry: {e}");
        Registry::new()
    })
});

/// Global telemetry tracer - simplified for `HFT` performance
// OpenTelemetry removed to reduce latency overhead in HFT system
/// TELEMETRY_ENABLED
pub static TELEMETRY_ENABLED: Lazy<bool> = Lazy::new(|| init_telemetry().is_ok());

/// Order acknowledgment latency histograms for P50/P95/P99 analysis
///
/// High-precision latency tracking using HDR histograms for percentile analysis.
/// Maintains separate histograms per venue/order_type combination for detailed
/// latency profiling across different trading venues and order types.
///
/// # Cardinality Control
/// Uses LRU cache with max 100 entries to prevent unbounded memory growth.
/// When cache is full, least recently used histograms are evicted.
///
/// # Thread Safety
/// Protected by RwLock for concurrent access from multiple trading threads.
pub static ORDER_ACK_LATENCY: Lazy<Arc<RwLock<LruCache<String, hdrhistogram::Histogram<u64>>>>> =
    Lazy::new(|| {
        Arc::new(RwLock::new(LruCache::new(
            NonZeroUsize::new(100).expect("Valid non-zero size"),
        )))
    });

/// Trading Business Metrics - Regular Prometheus counters
///
/// Core trading operation counters for monitoring business metrics:
/// - Order submissions, executions, cancellations
/// - Trade volumes and counts by instrument and venue
/// - Side-specific metrics (buy/sell) for flow analysis
///
/// # Cardinality Optimization
/// - **Legacy mode** (FOXHUNT_USE_OPTIMIZED_METRICS=`false`): [action, instrument, side, venue] = 500K+ series
/// - **Optimized mode** (FOXHUNT_USE_OPTIMIZED_METRICS=`true`): [action, asset_class, side, venue] = 5K series (99% reduction)
///
/// Labels (Legacy): [action, instrument, side, venue]
/// Labels (Optimized): [action, asset_class, side, venue]
pub static TRADING_COUNTERS: Lazy<IntCounterVec> = Lazy::new(|| {
    IntCounterVec::new(
        Opts::new(
            "foxhunt_trading_operations_total",
            "Trading operations counter",
        ),
        &["action", "asset_class", "side", "venue"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create trading counters: {e}");
        IntCounterVec::new(
            Opts::new(
                "foxhunt_trading_operations_fallback",
                "Fallback trading counter",
            ),
            &["action"],
        )
        .unwrap_or_else(|_| create_noop_int_counter_vec())
    })
});

/// Latency histograms for comprehensive system performance monitoring
///
/// Tracks latency distributions across all system components with microsecond
/// precision buckets optimized for `HFT` requirements. Essential for identifying
/// performance bottlenecks and ensuring sub-50μs latency targets.
///
/// Labels: [component, service]
pub static LATENCY_HISTOGRAMS: Lazy<HistogramVec> = Lazy::new(|| {
    HistogramVec::new(
        HistogramOpts::new("foxhunt_latency_seconds", "Component latency histogram")
            .buckets(LATENCY_BUCKETS.to_vec()),
        &["component", "service"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create latency histograms: {e}");
        HistogramVec::new(
            HistogramOpts::new("foxhunt_latency_fallback", "Fallback latency histogram"),
            &["component"],
        )
        .unwrap_or_else(|_| create_noop_histogram_vec())
    })
});

/// Throughput counters for measuring data processing rates
///
/// Monitors message rates, order processing throughput, and data ingestion
/// rates across different services. Critical for capacity planning and
/// performance optimization in high-frequency trading scenarios.
///
/// Labels: [data_type, service]
pub static THROUGHPUT_COUNTERS: Lazy<IntCounterVec> = Lazy::new(|| {
    IntCounterVec::new(
        Opts::new("foxhunt_throughput_total", "Throughput counters"),
        &["data_type", "service"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create throughput counters: {e}");
        IntCounterVec::new(
            Opts::new("foxhunt_throughput_fallback", "Fallback throughput counter"),
            &["data_type"],
        )
        .unwrap_or_else(|_| create_noop_int_counter_vec())
    })
});

/// Error counters with severity classification
///
/// Comprehensive error tracking across all services with severity levels
/// for effective alerting and monitoring. Enables rapid identification
/// of system issues and their impact classification.
///
/// Labels: [error_type, service, severity]
pub static ERROR_COUNTERS: Lazy<IntCounterVec> = Lazy::new(|| {
    IntCounterVec::new(
        Opts::new("foxhunt_errors_total", "Error counters"),
        &["error_type", "service", "severity"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create error counters: {e}");
        IntCounterVec::new(
            Opts::new("foxhunt_errors_fallback", "Fallback error counter"),
            &["error_type"],
        )
        .unwrap_or_else(|_| create_noop_int_counter_vec())
    })
});

/// Financial metrics gauges for P&L and portfolio monitoring
///
/// Real-time financial metrics including unrealized/realized P&L,
/// position values, and risk metrics. Essential for portfolio
/// management and risk monitoring in live trading.
///
/// Labels: [metric_type, currency, strategy]
pub static FINANCIAL_GAUGES: Lazy<GaugeVec> = Lazy::new(|| {
    GaugeVec::new(
        Opts::new("foxhunt_financial_metrics", "Financial metrics gauges"),
        &["metric_type", "currency", "strategy"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create financial gauges: {e}");
        GaugeVec::new(
            Opts::new("foxhunt_financial_fallback", "Fallback financial gauge"),
            &["metric_type"],
        )
        .unwrap_or_else(|_| create_noop_gauge_vec())
    })
});

/// Connection pool monitoring gauges
///
/// Tracks database and broker connection pool health including
/// active, idle, and waiting connection counts. Critical for
/// detecting connection leaks and capacity issues.
///
/// Labels: [pool_type, state, service]
pub static CONNECTION_POOL_GAUGES: Lazy<IntGaugeVec> = Lazy::new(|| {
    IntGaugeVec::new(
        Opts::new("foxhunt_connection_pool", "Connection pool gauges"),
        &["pool_type", "state", "service"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create connection pool gauges: {e}");
        IntGaugeVec::new(
            Opts::new("foxhunt_connection_fallback", "Fallback connection gauge"),
            &["pool_type"],
        )
        .unwrap_or_else(|_| create_noop_int_gauge_vec())
    })
});

/// Microsecond-precision latency buckets for `HFT` monitoring
const LATENCY_BUCKETS: &[f64] = &[
    0.000_001, // 1 microsecond
    0.000_005, // 5 microseconds
    0.00001,   // 10 microseconds
    0.00005,   // 50 microseconds
    0.0001,    // 100 microseconds
    0.0005,    // 500 microseconds
    0.001,     // 1 millisecond
    0.005,     // 5 milliseconds
    0.01,      // 10 milliseconds
    0.05,      // 50 milliseconds
    0.1,       // 100 milliseconds
    0.5,       // 500 milliseconds
    1.0,       // 1 second
    5.0,       // 5 seconds
];

/// Throughput buckets for high-frequency data
const THROUGHPUT_BUCKETS: &[f64] = &[
    1.0,
    10.0,
    100.0,
    1_000.0,
    10_000.0,
    50_000.0,
    100_000.0,
    250_000.0,
    500_000.0,
    1_000_000.0,
];

// All metrics are now defined above as static Lazy instances

/// Specialized order processing latency histogram
///
/// Dedicated histogram for order processing latency with venue-specific
/// tracking. Provides detailed latency analysis for order lifecycle
/// from submission to acknowledgment across different trading venues.
///
/// Labels: [service, order_type, venue]
pub static ORDER_LATENCY_HISTOGRAM: Lazy<HistogramVec> = Lazy::new(|| {
    HistogramVec::new(
        HistogramOpts::new("foxhunt_order_latency_seconds", "Order processing latency")
            .buckets(LATENCY_BUCKETS.to_vec()),
        &["service", "order_type", "venue"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create order latency histogram: {e}");
        HistogramVec::new(
            HistogramOpts::new("foxhunt_order_latency_fallback", "Fallback order latency"),
            &["service"],
        )
        .unwrap_or_else(|_| create_noop_histogram_vec())
    })
});

/// Market data throughput monitoring histogram
///
/// Measures market data processing rates and throughput across different
/// feeds and asset classes. Essential for monitoring market data pipeline
/// performance and detecting feed latency issues.
///
/// # Cardinality Optimization
/// - **Legacy mode**: [feed, symbol, data_type] = 150K+ series (unbounded symbols)
/// - **Optimized mode**: [feed, asset_class, data_type] = ~150 series (99% reduction)
///
/// Labels: [feed, asset_class, data_type]
pub static MARKET_DATA_THROUGHPUT: Lazy<HistogramVec> = Lazy::new(|| {
    HistogramVec::new(
        HistogramOpts::new("foxhunt_market_data_throughput", "Market data throughput")
            .buckets(THROUGHPUT_BUCKETS.to_vec()),
        &["feed", "asset_class", "data_type"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create market data throughput histogram: {e}");
        HistogramVec::new(
            HistogramOpts::new("foxhunt_market_data_fallback", "Fallback market data"),
            &["feed"],
        )
        .unwrap_or_else(|_| create_noop_histogram_vec())
    })
});

/// Active positions gauge for portfolio monitoring
///
/// Real-time tracking of active trading positions across strategies
/// and asset classes. Provides instant visibility into portfolio
/// exposure and position concentration risks.
///
/// Labels: [strategy, asset_class, currency]
pub static ACTIVE_POSITIONS: Lazy<IntGaugeVec> = Lazy::new(|| {
    IntGaugeVec::new(
        Opts::new("foxhunt_active_positions", "Number of active positions"),
        &["strategy", "asset_class", "currency"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create active positions gauge: {e}");
        IntGaugeVec::new(
            Opts::new("foxhunt_positions_fallback", "Fallback positions gauge"),
            &["strategy"],
        )
        .unwrap_or_else(|_| create_noop_int_gauge_vec())
    })
});

/// Memory usage monitoring by component
///
/// Tracks memory consumption across different services and memory types
/// (heap, stack, resident). Essential for detecting memory leaks and
/// optimizing memory allocation in performance-critical trading systems.
///
/// Labels: [service, memory_type]
pub static MEMORY_USAGE: Lazy<GaugeVec> = Lazy::new(|| {
    GaugeVec::new(
        Opts::new("foxhunt_memory_usage_bytes", "Memory usage by component"),
        &["service", "memory_type"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create memory usage gauge: {e}");
        GaugeVec::new(
            Opts::new("foxhunt_memory_fallback", "Fallback memory gauge"),
            &["service"],
        )
        .unwrap_or_else(|_| create_noop_gauge_vec())
    })
});

/// `CPU` usage monitoring by service and core
///
/// Per-core `CPU` utilization tracking for performance optimization
/// and `CPU` affinity monitoring. Critical for ensuring optimal
/// `CPU` resource allocation in `HFT` systems.
///
/// Labels: [service, core]
pub static CPU_USAGE: Lazy<GaugeVec> = Lazy::new(|| {
    GaugeVec::new(
        Opts::new("foxhunt_cpu_usage_percent", "CPU usage by service"),
        &["service", "core"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create CPU usage gauge: {e}");
        GaugeVec::new(
            Opts::new("foxhunt_cpu_fallback", "Fallback CPU gauge"),
            &["service"],
        )
        .unwrap_or_else(|_| create_noop_gauge_vec())
    })
});

/// `gRPC` request duration histogram
///
/// Measures `gRPC` call latencies across different services and methods.
/// Essential for monitoring inter-service communication performance
/// and identifying bottlenecks in distributed trading architecture.
///
/// Labels: [service, method, status]
pub static GRPC_REQUEST_DURATION: Lazy<HistogramVec> = Lazy::new(|| {
    HistogramVec::new(
        HistogramOpts::new(
            "foxhunt_grpc_request_duration_seconds",
            "GRPC request duration",
        )
        .buckets(LATENCY_BUCKETS.to_vec()),
        &["service", "method", "status"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create GRPC request duration histogram: {e}");
        HistogramVec::new(
            HistogramOpts::new("foxhunt_grpc_duration_fallback", "Fallback GRPC duration"),
            &["service"],
        )
        .unwrap_or_else(|_| create_noop_histogram_vec())
    })
});

/// Total `gRPC` requests counter
///
/// Counts all `gRPC` requests by service, method, and response status.
/// Provides insight into service usage patterns and error rates
/// in the distributed trading system architecture.
///
/// Labels: [service, method, status]
pub static GRPC_REQUESTS_TOTAL: Lazy<IntCounterVec> = Lazy::new(|| {
    IntCounterVec::new(
        Opts::new("foxhunt_grpc_requests_total", "Total GRPC requests"),
        &["service", "method", "status"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create GRPC requests counter: {e}");
        IntCounterVec::new(
            Opts::new("foxhunt_grpc_requests_fallback", "Fallback GRPC requests"),
            &["service"],
        )
        .unwrap_or_else(|_| create_noop_int_counter_vec())
    })
});

/// Active database connections gauge
///
/// Monitors active database connections across different databases
/// and connection pools. Critical for detecting connection leaks
/// and ensuring optimal database connectivity.
///
/// Labels: [service, database, pool]
pub static DB_CONNECTIONS_ACTIVE: Lazy<IntGaugeVec> = Lazy::new(|| {
    IntGaugeVec::new(
        Opts::new(
            "foxhunt_db_connections_active",
            "Active database connections",
        ),
        &["service", "database", "pool"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create DB connections gauge: {e}");
        IntGaugeVec::new(
            Opts::new("foxhunt_db_connections_fallback", "Fallback DB connections"),
            &["service"],
        )
        .unwrap_or_else(|_| create_noop_int_gauge_vec())
    })
});

/// Database query duration histogram
///
/// Tracks database query execution times by table and operation type.
/// Essential for identifying slow queries and optimizing database
/// performance in latency-sensitive trading operations.
///
/// Labels: [service, table, operation]
pub static DB_QUERY_DURATION: Lazy<HistogramVec> = Lazy::new(|| {
    HistogramVec::new(
        HistogramOpts::new(
            "foxhunt_db_query_duration_seconds",
            "Database query duration",
        )
        .buckets(LATENCY_BUCKETS.to_vec()),
        &["service", "table", "operation"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create DB query duration histogram: {e}");
        HistogramVec::new(
            HistogramOpts::new("foxhunt_db_query_fallback", "Fallback DB query duration"),
            &["service"],
        )
        .unwrap_or_else(|_| create_noop_histogram_vec())
    })
});

/// Circuit breaker state monitoring gauge
///
/// Tracks circuit breaker states across services (0=closed, 1=open, 2=half-open).
/// Essential for monitoring system resilience and automatic failure recovery
/// mechanisms in the trading infrastructure.
///
/// Labels: [service, breaker_name]
pub static CIRCUIT_BREAKER_STATE: Lazy<IntGaugeVec> = Lazy::new(|| {
    IntGaugeVec::new(
        Opts::new(
            "foxhunt_circuit_breaker_state",
            "Circuit breaker state (0=closed, 1=open, 2=half-open)",
        ),
        &["service", "breaker_name"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create circuit breaker state gauge: {e}");
        IntGaugeVec::new(
            Opts::new(
                "foxhunt_circuit_breaker_fallback",
                "Fallback circuit breaker",
            ),
            &["service"],
        )
        .unwrap_or_else(|_| create_noop_int_gauge_vec())
    })
});

/// Risk limit utilization ratio gauge
///
/// Monitors utilization of various risk limits as ratios (0.0-1.0).
/// Critical for risk management and preventing limit breaches
/// in automated trading strategies.
///
/// Labels: [limit_type, strategy, asset_class]
pub static RISK_LIMIT_UTILIZATION: Lazy<GaugeVec> = Lazy::new(|| {
    GaugeVec::new(
        Opts::new(
            "foxhunt_risk_limit_utilization_ratio",
            "Risk limit utilization ratio (0-1)",
        ),
        &["limit_type", "strategy", "asset_class"],
    )
    .unwrap_or_else(|e| {
        eprintln!("Failed to create risk limit utilization gauge: {e}");
        GaugeVec::new(
            Opts::new("foxhunt_risk_limit_fallback", "Fallback risk limit gauge"),
            &["limit_type"],
        )
        .unwrap_or_else(|_| create_noop_gauge_vec())
    })
});

/// Timer helper for measuring latencies.
///
/// Wraps a Prometheus [`Histogram`] together with a start [`Instant`] so that a
/// single call to [`observe_and_stop`](LatencyTimer::observe_and_stop) records the
/// elapsed wall-clock duration into the histogram. Designed for low-overhead,
/// scope-based latency instrumentation in HFT hot paths.
#[derive(Debug)]
pub struct LatencyTimer {
    start: Instant,
    histogram: Histogram,
}

impl LatencyTimer {
    /// Create a new latency timer with the specified histogram
    ///
    /// # Arguments
    /// * `histogram` - Prometheus histogram to record the latency measurement
    ///
    /// # Returns
    /// A new `LatencyTimer` instance that starts measuring immediately
    #[must_use]
    pub fn new(histogram: Histogram) -> Self {
        Self {
            start: Instant::now(),
            histogram,
        }
    }

    /// Stop the timer and record the elapsed time to the histogram
    ///
    /// # Returns
    /// The elapsed duration since timer creation
    ///
    /// # Examples
    /// ``rust
    /// let histogram = LATENCY_HISTOGRAMS.with_label_values(&["component", "service"]);
    /// let timer = LatencyTimer::new(histogram);
    /// // ... perform operation ...
    /// let duration = timer.observe_and_stop();
    /// ``
    #[must_use]
    pub fn observe_and_stop(self) -> Duration {
        let duration = self.start.elapsed();
        self.histogram.observe(duration.as_secs_f64());
        duration
    }
}

/// Record order submission to trading metrics
///
/// Records an order submission event with instrument, side, and venue labels.
/// This is a key business metric for monitoring trading activity.
///
/// # Cardinality Optimization
/// Automatically buckets instruments into asset classes (crypto, forex, equities, etc.)
/// to achieve 99% cardinality reduction when optimized metrics are enabled.
///
/// # Arguments
/// * `instrument` - Trading instrument symbol (e.g., "AAPL", "EURUSD", "BTCUSD")
/// * `side` - Order side ("buy" or "sell")
/// * `venue` - Trading venue identifier (e.g., "nasdaq", "interactive_brokers")
///
/// # Examples
/// ``rust
/// record_order_submitted("AAPL", "buy", "nasdaq");  // → asset_class: "equities"
/// record_order_submitted("BTCUSD", "buy", "binance"); // → asset_class: "crypto"
/// record_order_submitted("EURUSD", "sell", "forex.com"); // → asset_class: "forex"
/// ``
pub fn record_order_submitted(instrument: &str, side: &str, venue: &str) {
    let asset_class = bucket_instrument(instrument);
    TRADING_COUNTERS
        .with_label_values(&["orders_submitted", asset_class, side, venue])
        .inc();
}

/// Record trade execution with latency measurement
///
/// Records a successful trade execution along with its latency measurement.
/// This is critical for monitoring execution performance and latency.
///
/// # Cardinality Optimization
/// Automatically buckets instruments into asset classes for reduced cardinality.
///
/// # Arguments
/// * `latency_micros` - Execution latency in microseconds
/// * `venue` - Trading venue where execution occurred
/// * `instrument` - Trading instrument that was executed
///
/// # Examples
/// ``rust
/// record_trade_execution(45, "nasdaq", "AAPL"); // → asset_class: "equities"
/// ``
pub fn record_trade_execution(latency_micros: u64, venue: &str, instrument: &str) {
    let asset_class = bucket_instrument(instrument);
    TRADING_COUNTERS
        .with_label_values(&["trades_executed", asset_class, "buy", venue])
        .inc();
    LATENCY_HISTOGRAMS
        .with_label_values(&["execution_latency", "trading_engine"])
        .observe(latency_micros as f64 / 1_000_000.0);
}

/// Record market data message processing
///
/// Records the processing of a market data message with timing information.
/// Essential for monitoring market data pipeline performance.
///
/// # Arguments
/// * `_feed` - Market data feed identifier (currently unused)
/// * `_symbol` - Symbol for the market data (currently unused)
/// * `processing_time_nanos` - Processing time in nanoseconds
///
/// # Examples
/// ``rust
/// record_market_data_message("nasdaq", "AAPL", 1500);
/// ``
pub fn record_market_data_message(_feed: &str, _symbol: &str, processing_time_nanos: u64) {
    THROUGHPUT_COUNTERS
        .with_label_values(&["market_data_messages", "market_data"])
        .inc();
    // LATENCY_HISTOGRAMS variant
    LATENCY_HISTOGRAMS
        .with_label_values(&["market_data_ingestion", "market_data"])
        .observe(processing_time_nanos as f64 / 1_000_000_000.0);
}

/// Record error with context and severity
///
/// Records an error event with service context, error type, and severity level.
/// Used for comprehensive error tracking and alerting.
///
/// # Arguments
/// * `service` - Service where the error occurred
/// * `error_type` - Type/category of the error
/// * `severity` - Error severity level ("low", "medium", "high", "critical")
///
/// # Examples
/// ``rust
/// record_error("trading_service", "connection_timeout", "high");
/// ``
pub fn record_error(service: &str, error_type: &str, severity: &str) {
    ERROR_COUNTERS
        .with_label_values(&[error_type, service, severity])
        .inc();
}

/// Update P&L (Profit and Loss) metrics
///
/// Updates both unrealized and realized P&L metrics for a given strategy
/// and currency. Essential for real-time portfolio monitoring.
///
/// # Arguments
/// * `strategy` - Trading strategy identifier
/// * `currency` - Currency denomination (e.g., "USD", "EUR")
/// * `unrealized` - Unrealized P&L amount
/// * `realized` - Realized P&L amount
///
/// # Examples
/// ``rust
/// update_pnl("momentum_strategy", "USD", 1250.75, 890.50);
/// ``
pub fn update_pnl(strategy: &str, currency: &str, unrealized: f64, realized: f64) {
    FINANCIAL_GAUGES
        .with_label_values(&["unrealized_pnl", currency, strategy])
        .set(unrealized);
    // FINANCIAL_GAUGES variant
    FINANCIAL_GAUGES
        .with_label_values(&["realized_pnl", currency, strategy])
        .set(realized);
}

/// Update connection pool metrics
///
/// Updates connection pool state metrics including active, idle, and waiting
/// connection counts. Critical for monitoring database and broker connectivity.
///
/// # Arguments
/// * `service` - Service owning the connection pool
/// * `pool_type` - Type of connection pool ("database", "broker", etc.)
/// * `active` - Number of active connections
/// * `idle` - Number of idle connections
/// * `waiting` - Number of connections waiting in queue
///
/// # Examples
/// ``rust
/// update_connection_pool("trading_service", "database", 5, 3, 0);
/// ``
pub fn update_connection_pool(
    service: &str,
    pool_type: &str,
    active: i64,
    idle: i64,
    waiting: i64,
) {
    CONNECTION_POOL_GAUGES
        .with_label_values(&[pool_type, "active", service])
        .set(active);
    // CONNECTION_POOL_GAUGES variant
    CONNECTION_POOL_GAUGES
        .with_label_values(&[pool_type, "idle", service])
        .set(idle);
    // CONNECTION_POOL_GAUGES variant
    CONNECTION_POOL_GAUGES
        .with_label_values(&[pool_type, "waiting", service])
        .set(waiting);
}

/// Initialize telemetry system for `HFT` performance
///
/// Initializes the telemetry system with minimal overhead optimized for
/// high-frequency trading. OpenTelemetry was removed to reduce latency.
///
/// # Returns
/// * `Ok`(())` - Telemetry initialized successfully
/// * `Err`(_)` - Telemetry initialization failed
///
/// # Examples
/// ``rust
/// if let `Err`(e) = init_telemetry() {
///     eprintln!("Failed to initialize telemetry: {}", e);
/// }
/// ``
pub fn init_telemetry() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // Simple initialization without OpenTelemetry dependencies
    // Using native tracing instead for minimal overhead
    tracing::info!("Telemetry initialized with simple tracing");
    Ok(())
}

/// Record order acknowledgment latency with P50/P95/P99 analysis
///
/// Records order acknowledgment latency using HDR histograms for precise
/// percentile analysis. Maintains separate histograms per venue/order_type
/// for detailed latency profiling.
///
/// # Arguments
/// * `venue` - Trading venue identifier
/// * `order_type` - Type of order ("market", "limit", "stop", etc.)
/// * `latency_ns` - Latency in nanoseconds
///
/// # Examples
/// ``rust
/// record_order_ack_latency("nasdaq", "limit", 25_000); // 25 microseconds
/// ``
pub fn record_order_ack_latency(venue: &str, order_type: &str, latency_ns: u64) {
    let key = format!("{venue}_{order_type}");

    // Get or create histogram for this venue/order_type combination
    {
        let mut histograms = ORDER_ACK_LATENCY.write();
        if let Some(histogram) = histograms.get_mut(&key) {
            // Record the latency (convert to microseconds for HDR histogram)
            let latency_us_existing = latency_ns / 1000;
            if let Err(e) = histogram.record(latency_us_existing) {
                tracing::warn!("Failed to record latency for {}: {}", key, e);
            }
            return;
        }
    }

    // Create new histogram if it doesn't exist
    {
        let mut histograms = ORDER_ACK_LATENCY.write();

        // Try to create histogram with multiple fallback strategies
        let histogram_result = hdrhistogram::Histogram::new_with_bounds(1, 100_000, 3)
            .or_else(|e| {
                tracing::error!("Failed to create histogram for {}: {}", key, e);
                hdrhistogram::Histogram::new(3)
            })
            .or_else(|e2| {
                tracing::error!(
                    "Failed to create fallback histogram (3 digits) for {}: {}",
                    key,
                    e2
                );
                hdrhistogram::Histogram::new(2)
            })
            .or_else(|e3| {
                tracing::error!(
                    "Failed to create fallback histogram (2 digits) for {}: {}",
                    key,
                    e3
                );
                hdrhistogram::Histogram::new(1)
            });

        // If all attempts failed, log error and skip histogram creation
        if let Ok(histogram) = histogram_result {
            // LRU cache API: use push() instead of insert()
            histograms.push(key.clone(), histogram);
        } else {
            tracing::error!(
                "CRITICAL: All HDR histogram creation attempts failed for {}. \
                Latency percentiles will not be available for this venue/order_type combination.",
                key
            );
            // Don't insert anything - latency recording will be skipped
            return;
        }

        // Record the latency
        if let Some(histogram) = histograms.get_mut(&key) {
            let latency_us_new = latency_ns / 1000;
            if let Err(e) = histogram.record(latency_us_new) {
                tracing::warn!("Failed to record latency for {}: {}", key, e);
            }
        }
    }

    // Also record in Prometheus histogram for compatibility
    ORDER_LATENCY_HISTOGRAM
        .with_label_values(&["trading_service", order_type, venue])
        .observe(latency_ns as f64 / 1_000_000_000.0);
}

/// Get P50/P95/P99 latency percentiles for order acknowledgments
///
/// Retrieves latency percentile statistics for a specific venue and order type.
/// Returns `None` if no data has been recorded for the specified combination.
///
/// # Arguments
/// * `venue` - Trading venue identifier
/// * `order_type` - Type of order
///
/// # Returns
/// * `Some`(LatencyPercentiles)` - Percentile statistics if data exists
/// * `None` - No data recorded for this venue/order_type combination
///
/// # Examples
/// ``rust
/// if let `Some`(stats) = get_order_ack_percentiles("nasdaq", "limit") {
///     println!("P95 latency: {} microseconds", stats.p95_us);
/// }
/// ``
pub fn get_order_ack_percentiles(venue: &str, order_type: &str) -> Option<LatencyPercentiles> {
    let key = format!("{venue}_{order_type}");
    let histograms = ORDER_ACK_LATENCY.read();

    histograms.peek(&key).map(|histogram| LatencyPercentiles {
        p50_us: histogram.value_at_percentile(50.0),
        p95_us: histogram.value_at_percentile(95.0),
        p99_us: histogram.value_at_percentile(99.0),
        max_us: histogram.max(),
        min_us: histogram.min(),
        count: histogram.len(),
    })
}

/// Latency percentile statistics capturing the full distribution of measured durations.
///
/// All latency values are in **microseconds** (us) for consistency across trading
/// operations. Populated from an HDR histogram, this struct exposes the key
/// percentiles (p50, p95, p99) plus min/max bounds and the total sample count.
/// Used by the order acknowledgment latency subsystem to report venue-level
/// performance to Prometheus and monitoring dashboards.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LatencyPercentiles {
    /// 50th percentile latency in microseconds (median)
    ///
    /// Half of all operations complete faster than this latency.
    /// This is the typical latency experienced by most operations.
    pub p50_us: u64,

    /// 95th percentile latency in microseconds
    ///
    /// 95% of all operations complete faster than this latency.
    /// This represents the worst-case latency for most operations,
    /// excluding the most extreme outliers.
    pub p95_us: u64,

    /// 99th percentile latency in microseconds
    ///
    /// 99% of all operations complete faster than this latency.
    /// This captures near worst-case performance including
    /// most operational anomalies.
    pub p99_us: u64,

    /// Maximum latency observed in microseconds
    ///
    /// The absolute worst-case latency recorded during the
    /// measurement period. This helps identify severe
    /// performance degradations or system issues.
    pub max_us: u64,

    /// Minimum latency observed in microseconds
    ///
    /// The best-case latency recorded during the measurement
    /// period. This represents optimal system performance
    /// under ideal conditions.
    pub min_us: u64,

    /// Total number of samples collected
    ///
    /// The number of latency measurements used to calculate
    /// these percentiles. Higher counts provide more accurate
    /// statistical measurements.
    pub count: u64,
}

/// Export order acknowledgment percentiles to Prometheus
///
/// Exports HDR histogram percentiles as Prometheus gauges for external
/// monitoring and alerting. Should be called periodically to update
/// the exported metrics.
///
/// # Side Effects
/// Updates Prometheus gauges with current percentile values from HDR histograms
///
/// # Examples
/// ``rust
/// // Call periodically (e.g., every 30 seconds)
/// export_order_ack_percentiles_to_prometheus();
/// ``
pub fn export_order_ack_percentiles_to_prometheus() {
    let histograms = ORDER_ACK_LATENCY.read();

    for (key, histogram) in histograms.iter() {
        let parts: Vec<&str> = key.split('_').collect();
        if parts.len() >= 2 {
            let venue = parts[0];
            let order_type = parts[1..].join("_");

            // Export percentiles as separate Prometheus gauges
            if let Ok(p50_gauge) = FINANCIAL_GAUGES.get_metric_with_label_values(&[
                "order_ack_p50_us",
                venue,
                &order_type,
            ]) {
                p50_gauge.set(histogram.value_at_percentile(50.0) as f64);
            }
            if let Ok(p95_gauge) = FINANCIAL_GAUGES.get_metric_with_label_values(&[
                "order_ack_p95_us",
                venue,
                &order_type,
            ]) {
                p95_gauge.set(histogram.value_at_percentile(95.0) as f64);
            }
            if let Ok(p99_gauge) = FINANCIAL_GAUGES.get_metric_with_label_values(&[
                "order_ack_p99_us",
                venue,
                &order_type,
            ]) {
                p99_gauge.set(histogram.value_at_percentile(99.0) as f64);
            }
        }
    }
}

/// Create telemetry span for critical trading operations
///
/// Creates a tracing span for critical trading operations. Simplified for
/// `HFT` performance using standard tracing instead of OpenTelemetry to
/// minimize latency overhead.
///
/// # Arguments
/// * `operation` - Trading operation name
/// * `venue` - Trading venue identifier
///
/// # Examples
/// ``rust
/// create_trading_span("order_submission", "nasdaq");
/// ``
pub fn create_trading_span(operation: &str, venue: &str) {
    if *TELEMETRY_ENABLED {
        // Use standard tracing for minimal overhead
        tracing::debug!(
            "Trading operation: operation={}, venue={}",
            operation,
            venue
        );
    } else {
        // Fallback logging when telemetry is disabled
        tracing::debug!(
            "Trading operation (telemetry disabled): operation={}, venue={}",
            operation,
            venue
        );
    }
}

/// Market data event flattened for Parquet columnar storage.
///
/// All nested types are expanded into primitive or `Option` fields so the struct
/// maps directly to a Parquet schema without intermediate encoding. Used by the
/// data acquisition pipeline to persist raw market data for offline backtesting,
/// analytics, and ML feature extraction. Events are buffered in
/// [`MARKET_DATA_BUFFER`] and flushed in batches to Parquet files on disk.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ParquetMarketDataEvent {
    /// Event timestamp in nanoseconds since Unix epoch
    ///
    /// High-precision timestamp for accurate event ordering
    /// and latency calculations in analytics workflows.
    pub timestamp_ns: u64,

    /// Trading symbol identifier (e.g., "BTCUSD", "AAPL")
    ///
    /// Standardized symbol representation for cross-venue
    /// aggregation and normalization.
    pub symbol: String,

    /// Trading venue or exchange identifier
    ///
    /// Source exchange for venue-specific analytics and
    /// latency arbitrage calculations.
    pub venue: String,

    /// Type of market data event
    ///
    /// Categorizes the event for efficient filtering and
    /// processing in analytics pipelines.
    pub event_type: MarketDataEventType,

    /// Price level for the event (if applicable)
    ///
    /// Optional field as not all market data events have
    /// price information (e.g., status updates).
    pub price: Option<f64>,

    /// Quantity/size for the event (if applicable)
    ///
    /// Optional field for volume-based analytics and
    /// order book reconstruction.
    pub quantity: Option<f64>,

    /// Sequence number for event ordering
    ///
    /// Ensures correct chronological ordering within
    /// each venue's event stream.
    pub sequence: u64,

    /// Processing latency in nanoseconds (if measured)
    ///
    /// Optional field for performance monitoring and
    /// system optimization analytics.
    pub latency_ns: Option<u64>,

    /// Open price for OHLCV bars (if applicable)
    ///
    /// First price in the bar's time period. Only populated for
    /// Ohlcv event types, None for Trade/Quote/OrderBook events.
    pub open: Option<f64>,

    /// High price for OHLCV bars (if applicable)
    ///
    /// Highest price in the bar's time period. Only populated for
    /// Ohlcv event types, None for Trade/Quote/OrderBook events.
    pub high: Option<f64>,

    /// Low price for OHLCV bars (if applicable)
    ///
    /// Lowest price in the bar's time period. Only populated for
    /// Ohlcv event types, None for Trade/Quote/OrderBook events.
    pub low: Option<f64>,
}

/// Market data event classification for routing and analytics processing.
///
/// Categorizes inbound market data into discrete event types so downstream
/// consumers (backtesting engine, feature extractors, order book reconstructors)
/// can filter efficiently. Each variant maps to a distinct processing path:
/// trades feed execution analytics, quotes feed spread monitoring, order book
/// updates feed depth-of-market analysis, and status updates drive session
/// state machines.
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum MarketDataEventType {
    /// Executed trade event
    ///
    /// Represents an actual trade execution with price and volume.
    /// Used for trade-based analytics and execution quality metrics.
    Trade,

    /// Quote update event (bid/ask)
    ///
    /// Represents top-of-book price and size updates.
    /// Used for spread analysis and liquidity monitoring.
    Quote,

    /// Order book depth update
    ///
    /// Represents changes to market depth beyond top-of-book.
    /// Used for order book analytics and liquidity analysis.
    OrderBookUpdate,

    /// Market status or system update
    ///
    /// Represents non-price events like market open/close,
    /// trading halts, or system status changes.
    StatusUpdate,

    /// OHLCV bar data event
    ///
    /// Represents aggregated bar/candlestick data with open, high,
    /// low, close prices and volume. Used for backtesting and
    /// time-series analytics.
    Ohlcv,
}

/// Market data buffer for Parquet batching
pub static MARKET_DATA_BUFFER: Lazy<Arc<RwLock<Vec<ParquetMarketDataEvent>>>> =
    Lazy::new(|| Arc::new(RwLock::new(Vec::with_capacity(10000))));

/// Record market data event for Parquet persistence
///
/// Records a market data event in the buffer for batch writing to Parquet files.
/// Automatically triggers flush warnings when buffer approaches capacity.
///
/// # Arguments
/// * `event` - Market data event to record
///
/// # Side Effects
/// - Adds event to internal buffer
/// - Logs warning when buffer reaches 8000 events (80% capacity)
///
/// # Examples
/// ``rust
/// let event = ParquetMarketDataEvent {
///     timestamp_ns: 1234567890,
///     symbol: "AAPL".to_string(),
///     venue: "nasdaq".to_string(),
///     event_type: MarketDataEventType::Trade,
///     price: `Some`(150.25),
///     quantity: `Some`(100.0),
///     sequence: 12345,
///     latency_ns: `Some`(1500),
/// };
/// record_market_data_event(event);
/// ``
pub fn record_market_data_event(event: ParquetMarketDataEvent) {
    let mut buffer = MARKET_DATA_BUFFER.write();
    buffer.push(event);

    // Trigger flush if buffer is getting full
    if buffer.len() >= 8000 {
        // In a real implementation, this would trigger async Parquet write
        tracing::info!("Market data buffer near capacity: {} events", buffer.len());
    }
}

/// Initialize all metrics on startup
///
/// Registers all metric collectors with the global Prometheus registry.
/// Must be called once during application startup before using any metrics.
///
/// # Returns
/// * `Ok`(())` - All metrics registered successfully
/// * `Err`(_)` - Metric registration failed
///
/// # Examples
/// ``rust
/// match initialize_metrics() {
///     `Ok`(()) => println!("Metrics initialized successfully"),
///     `Err`(e) => eprintln!("Failed to initialize metrics: {}", e),
/// }
/// ``
pub fn initialize_metrics() -> PrometheusResult<()> {
    let registry = &*METRICS_REGISTRY;

    // Register all metrics
    registry.register(Box::new(TRADING_COUNTERS.clone()))?;
    registry.register(Box::new(LATENCY_HISTOGRAMS.clone()))?;
    registry.register(Box::new(THROUGHPUT_COUNTERS.clone()))?;
    registry.register(Box::new(ERROR_COUNTERS.clone()))?;
    registry.register(Box::new(FINANCIAL_GAUGES.clone()))?;
    registry.register(Box::new(CONNECTION_POOL_GAUGES.clone()))?;
    registry.register(Box::new(ORDER_LATENCY_HISTOGRAM.clone()))?;
    registry.register(Box::new(MARKET_DATA_THROUGHPUT.clone()))?;
    registry.register(Box::new(ACTIVE_POSITIONS.clone()))?;
    registry.register(Box::new(MEMORY_USAGE.clone()))?;
    registry.register(Box::new(CPU_USAGE.clone()))?;
    registry.register(Box::new(GRPC_REQUEST_DURATION.clone()))?;
    registry.register(Box::new(GRPC_REQUESTS_TOTAL.clone()))?;
    registry.register(Box::new(DB_CONNECTIONS_ACTIVE.clone()))?;
    registry.register(Box::new(DB_QUERY_DURATION.clone()))?;
    registry.register(Box::new(CIRCUIT_BREAKER_STATE.clone()))?;
    registry.register(Box::new(RISK_LIMIT_UTILIZATION.clone()))?;

    Ok(())
}

/// Get metrics output for Prometheus scraping
///
/// Generates the Prometheus metrics output in text format for scraping
/// by monitoring systems. Returns all registered metrics in the
/// standard Prometheus exposition format.
///
/// # Returns
/// Prometheus metrics in text format, or empty string on encoding failure
///
/// # Examples
/// ``rust
/// let metrics = get_metrics_output();
/// // Serve this string on /metrics endpoint
/// ``
pub fn get_metrics_output() -> String {
    let encoder = prometheus::TextEncoder::new();
    let metric_families = METRICS_REGISTRY.gather();
    encoder
        .encode_to_string(&metric_families)
        .unwrap_or_else(|e| {
            tracing::error!("Failed to encode metrics: {}", e);
            String::new()
        })
}

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

    #[test]
    fn test_metrics_initialization() {
        // initialize_metrics() returns AlreadyReg if another test in this
        // process already triggered the lazy_static counters. Both Ok and
        // AlreadyReg are acceptable — only a hard error would be a bug.
        let result = initialize_metrics();
        assert!(
            result.is_ok()
                || result
                    .as_ref()
                    .err()
                    .is_some_and(|e| format!("{e}").contains("already")),
            "initialize_metrics failed with unexpected error: {result:?}"
        );
    }

    #[test]
    fn test_trading_metrics() {
        // TRADING_COUNTERS variant (using asset_class now)
        TRADING_COUNTERS
            .with_label_values(&["orders_submitted", "equities", "buy", "interactive_brokers"])
            .inc();
        // Metrics should not panic
    }

    #[test]
    fn test_latency_timer() {
        let histogram =
            LATENCY_HISTOGRAMS.with_label_values(&["order_processing", "trading_engine"]);
        let timer = LatencyTimer::new(histogram);
        std::thread::sleep(Duration::from_millis(1));
        let duration = timer.observe_and_stop();
        assert!(duration.as_millis() >= 1);
    }

    #[test]
    fn test_metrics_output() {
        // Initialize metrics registry before gathering output
        // Ignore error if metrics are already registered (from other tests)
        let _ = initialize_metrics();

        // Record some sample metrics to ensure registry has data
        TRADING_COUNTERS
            .with_label_values(&["test_metric", "test_asset", "buy", "test_venue"])
            .inc();

        let output = get_metrics_output();
        assert!(
            !output.is_empty(),
            "Metrics output should contain data after initialization and recording"
        );
    }
}
