//! OpenTelemetry Tracing for Broker Gateway Service
//!
//! This module provides distributed tracing with OpenTelemetry for all RPC methods,
//! FIX protocol operations, and error tracking. Spans are correlated across
//! Trading Agent Service, Broker Gateway Service, and Trading Service.
//!
//! ## Trace Hierarchy
//!
//! ```text
//! Trading Agent Decision Loop
//! └── BrokerGateway::route_order
//!     ├── validate_order
//!     ├── generate_client_order_id
//!     ├── database_insert
//!     └── fix_send_order (Phase 2)
//!         ├── fix_encode_new_order_single
//!         ├── fix_network_send
//!         └── fix_await_execution_report
//! ```
//!
//! ## Integration
//!
//! Traces are exported to Jaeger/Grafana Tempo for distributed analysis.

#![deny(warnings)]

use tracing::{error, info, warn, Span};
use std::time::Instant;

/// Span attribute keys for structured tracing
pub mod attributes {
    pub const SERVICE_NAME: &str = "broker_gateway_service";
    pub const SERVICE_VERSION: &str = env!("CARGO_PKG_VERSION");

    // Order attributes
    pub const ORDER_SYMBOL: &str = "order.symbol";
    pub const ORDER_SIDE: &str = "order.side";
    pub const ORDER_TYPE: &str = "order.type";
    pub const ORDER_QUANTITY: &str = "order.quantity";
    pub const ORDER_PRICE: &str = "order.price";
    pub const ORDER_CLIENT_ID: &str = "order.client_id";
    pub const ORDER_BROKER_ID: &str = "order.broker_id";
    pub const ORDER_STATUS: &str = "order.status";

    // Account attributes
    pub const ACCOUNT_ID: &str = "account.id";
    pub const ACCOUNT_BALANCE: &str = "account.balance";
    pub const ACCOUNT_MARGIN_USED: &str = "account.margin_used";

    // FIX session attributes
    pub const FIX_SESSION_ID: &str = "fix.session_id";
    pub const FIX_MESSAGE_TYPE: &str = "fix.message_type";
    pub const FIX_SENDER_SEQ: &str = "fix.sender_seq";
    pub const FIX_TARGET_SEQ: &str = "fix.target_seq";
    pub const FIX_HEARTBEAT_RTT_MS: &str = "fix.heartbeat_rtt_ms";

    // Error attributes
    pub const ERROR_TYPE: &str = "error.type";
    pub const ERROR_MESSAGE: &str = "error.message";
    pub const ERROR_STACK_TRACE: &str = "error.stack_trace";
    pub const ERROR_SEVERITY: &str = "error.severity";

    // Performance attributes
    pub const LATENCY_MS: &str = "latency.ms";
    pub const DATABASE_QUERY_TIME_MS: &str = "database.query_time_ms";
    pub const FIX_NETWORK_TIME_MS: &str = "fix.network_time_ms";
}

/// Create a new span for order routing RPC
///
/// # Arguments
/// * `symbol` - Trading symbol
/// * `side` - Order side (BUY, SELL)
/// * `order_type` - Order type
/// * `quantity` - Order quantity
///
/// # Returns
/// Configured tracing span
pub fn span_route_order(
    symbol: &str,
    side: &str,
    order_type: &str,
    quantity: f64,
) -> Span {
    let span = tracing::info_span!(
        "broker_gateway.route_order",
        service.name = attributes::SERVICE_NAME,
        service.version = attributes::SERVICE_VERSION,
        order.symbol = symbol,
        order.side = side,
        order.type = order_type,
        order.quantity = quantity,
    );

    info!(
        parent: &span,
        "RouteOrder RPC called: symbol={}, side={}, type={}, qty={}",
        symbol, side, order_type, quantity
    );

    span
}

/// Create a new span for order cancellation RPC
///
/// # Arguments
/// * `client_order_id` - Client order ID
/// * `account_id` - Account identifier
///
/// # Returns
/// Configured tracing span
pub fn span_cancel_order(client_order_id: &str, account_id: &str) -> Span {
    let span = tracing::info_span!(
        "broker_gateway.cancel_order",
        service.name = attributes::SERVICE_NAME,
        service.version = attributes::SERVICE_VERSION,
        order.client_id = client_order_id,
        account.id = account_id,
    );

    info!(
        parent: &span,
        "CancelOrder RPC called: client_order_id={}, account_id={}",
        client_order_id, account_id
    );

    span
}

/// Create a new span for account state query RPC
///
/// # Arguments
/// * `account_id` - Account identifier
///
/// # Returns
/// Configured tracing span
pub fn span_get_account_state(account_id: &str) -> Span {
    let span = tracing::info_span!(
        "broker_gateway.get_account_state",
        service.name = attributes::SERVICE_NAME,
        service.version = attributes::SERVICE_VERSION,
        account.id = account_id,
    );

    info!(
        parent: &span,
        "GetAccountState RPC called: account_id={}",
        account_id
    );

    span
}

/// Create a new span for positions query RPC
///
/// # Arguments
/// * `account_id` - Account identifier
/// * `symbol` - Optional symbol filter
///
/// # Returns
/// Configured tracing span
pub fn span_get_positions(account_id: &str, symbol: Option<&str>) -> Span {
    let span = tracing::info_span!(
        "broker_gateway.get_positions",
        service.name = attributes::SERVICE_NAME,
        service.version = attributes::SERVICE_VERSION,
        account.id = account_id,
        order.symbol = symbol.unwrap_or("ALL"),
    );

    info!(
        parent: &span,
        "GetPositions RPC called: account_id={}, symbol={:?}",
        account_id, symbol
    );

    span
}

/// Create a new span for FIX session status query RPC
///
/// # Arguments
/// * `session_id` - FIX session identifier
///
/// # Returns
/// Configured tracing span
pub fn span_get_session_status(session_id: &str) -> Span {
    let span = tracing::info_span!(
        "broker_gateway.get_session_status",
        service.name = attributes::SERVICE_NAME,
        service.version = attributes::SERVICE_VERSION,
        fix.session_id = session_id,
    );

    info!(
        parent: &span,
        "GetSessionStatus RPC called: session_id={}",
        session_id
    );

    span
}

/// Create a new span for execution stream RPC
///
/// # Arguments
/// * `account_id` - Optional account filter
/// * `symbol` - Optional symbol filter
///
/// # Returns
/// Configured tracing span
pub fn span_stream_executions(account_id: Option<&str>, symbol: Option<&str>) -> Span {
    let span = tracing::info_span!(
        "broker_gateway.stream_executions",
        service.name = attributes::SERVICE_NAME,
        service.version = attributes::SERVICE_VERSION,
        account.id = account_id.unwrap_or("ALL"),
        order.symbol = symbol.unwrap_or("ALL"),
    );

    info!(
        parent: &span,
        "StreamExecutions RPC called: account_id={:?}, symbol={:?}",
        account_id, symbol
    );

    span
}

/// Create a new span for database operations
///
/// # Arguments
/// * `operation` - Database operation (INSERT, UPDATE, SELECT, DELETE)
/// * `table` - Database table name
///
/// # Returns
/// Configured tracing span
pub fn span_database_operation(operation: &str, table: &str) -> Span {
    tracing::debug_span!(
        "database.operation",
        db.operation = operation,
        db.table = table,
        db.system = "postgresql",
    )
}

/// Create a new span for FIX message encoding
///
/// # Arguments
/// * `message_type` - FIX message type
///
/// # Returns
/// Configured tracing span
pub fn span_fix_encode(message_type: &str) -> Span {
    tracing::debug_span!(
        "fix.encode",
        fix.message_type = message_type,
        fix.protocol = "FIX.4.4",
    )
}

/// Create a new span for FIX message network send
///
/// # Arguments
/// * `message_type` - FIX message type
/// * `session_id` - FIX session identifier
///
/// # Returns
/// Configured tracing span
pub fn span_fix_network_send(message_type: &str, session_id: &str) -> Span {
    tracing::debug_span!(
        "fix.network_send",
        fix.message_type = message_type,
        fix.session_id = session_id,
        network.transport = "tcp",
    )
}

/// Create a new span for FIX message receive
///
/// # Arguments
/// * `message_type` - FIX message type
/// * `session_id` - FIX session identifier
///
/// # Returns
/// Configured tracing span
pub fn span_fix_receive(message_type: &str, session_id: &str) -> Span {
    tracing::debug_span!(
        "fix.receive",
        fix.message_type = message_type,
        fix.session_id = session_id,
    )
}

/// Record error in current span with structured attributes
///
/// # Arguments
/// * `error_type` - Error classification
/// * `error_message` - Error description
/// * `severity` - Error severity (CRITICAL, ERROR, WARNING)
pub fn record_error(error_type: &str, error_message: &str, severity: &str) {
    let span = Span::current();
    span.record(attributes::ERROR_TYPE, error_type);
    span.record(attributes::ERROR_MESSAGE, error_message);
    span.record(attributes::ERROR_SEVERITY, severity);

    match severity {
        "CRITICAL" => error!(
            error.type = error_type,
            error.message = error_message,
            "Critical error occurred"
        ),
        "ERROR" => error!(
            error.type = error_type,
            error.message = error_message,
            "Error occurred"
        ),
        "WARNING" => warn!(
            error.type = error_type,
            error.message = error_message,
            "Warning occurred"
        ),
        _ => warn!(
            error.type = error_type,
            error.message = error_message,
            "Unknown severity: {}",
            severity
        ),
    }
}

/// Record order submission in current span
///
/// # Arguments
/// * `client_order_id` - Generated client order ID
/// * `status` - Order status
pub fn record_order_submitted(client_order_id: &str, status: &str) {
    let span = Span::current();
    span.record(attributes::ORDER_CLIENT_ID, client_order_id);
    span.record(attributes::ORDER_STATUS, status);

    info!(
        order.client_id = client_order_id,
        order.status = status,
        "Order submitted successfully"
    );
}

/// Record order fill in current span
///
/// # Arguments
/// * `broker_order_id` - Broker-assigned order ID
/// * `filled_quantity` - Quantity filled
/// * `fill_price` - Execution price
pub fn record_order_filled(broker_order_id: &str, filled_quantity: f64, fill_price: f64) {
    let span = Span::current();
    span.record(attributes::ORDER_BROKER_ID, broker_order_id);
    span.record(attributes::ORDER_STATUS, "FILLED");

    info!(
        order.broker_id = broker_order_id,
        order.filled_quantity = filled_quantity,
        order.fill_price = fill_price,
        "Order filled"
    );
}

/// Record order rejection in current span
///
/// # Arguments
/// * `reason` - Rejection reason
pub fn record_order_rejected(reason: &str) {
    let span = Span::current();
    span.record(attributes::ORDER_STATUS, "REJECTED");
    span.record(attributes::ERROR_TYPE, "ORDER_REJECTED");
    span.record(attributes::ERROR_MESSAGE, reason);

    warn!(
        order.status = "REJECTED",
        rejection.reason = reason,
        "Order rejected"
    );
}

/// Record database query latency in current span
///
/// # Arguments
/// * `start` - Query start time
pub fn record_database_latency(start: Instant) {
    let latency_ms = start.elapsed().as_secs_f64() * 1000.0;
    let span = Span::current();
    span.record(attributes::DATABASE_QUERY_TIME_MS, latency_ms);

    if latency_ms > 100.0 {
        warn!(
            database.query_time_ms = latency_ms,
            "Slow database query detected (>100ms)"
        );
    }
}

/// Record FIX network latency in current span
///
/// # Arguments
/// * `start` - Network send start time
pub fn record_fix_network_latency(start: Instant) {
    let latency_ms = start.elapsed().as_secs_f64() * 1000.0;
    let span = Span::current();
    span.record(attributes::FIX_NETWORK_TIME_MS, latency_ms);

    if latency_ms > 10.0 {
        warn!(
            fix.network_time_ms = latency_ms,
            "High FIX network latency detected (>10ms)"
        );
    }
}

/// Record FIX sequence numbers in current span
///
/// # Arguments
/// * `sender_seq` - Sender sequence number
/// * `target_seq` - Target sequence number
pub fn record_fix_sequence_numbers(sender_seq: i64, target_seq: i64) {
    let span = Span::current();
    span.record(attributes::FIX_SENDER_SEQ, sender_seq);
    span.record(attributes::FIX_TARGET_SEQ, target_seq);
}

/// Record FIX heartbeat RTT in current span
///
/// # Arguments
/// * `rtt_ms` - Round-trip time in milliseconds
pub fn record_fix_heartbeat_rtt(rtt_ms: f64) {
    let span = Span::current();
    span.record(attributes::FIX_HEARTBEAT_RTT_MS, rtt_ms);

    if rtt_ms > 50.0 {
        warn!(
            fix.heartbeat_rtt_ms = rtt_ms,
            "High FIX heartbeat RTT detected (>50ms)"
        );
    }
}

/// Record account state in current span
///
/// # Arguments
/// * `balance` - Account balance
/// * `margin_used` - Margin used
pub fn record_account_state(balance: f64, margin_used: f64) {
    let span = Span::current();
    span.record(attributes::ACCOUNT_BALANCE, balance);
    span.record(attributes::ACCOUNT_MARGIN_USED, margin_used);

    info!(
        account.balance = balance,
        account.margin_used = margin_used,
        "Account state retrieved"
    );
}

/// Record operation latency in current span
///
/// # Arguments
/// * `start` - Operation start time
pub fn record_latency(start: Instant) {
    let latency_ms = start.elapsed().as_secs_f64() * 1000.0;
    let span = Span::current();
    span.record(attributes::LATENCY_MS, latency_ms);

    info!(latency.ms = latency_ms, "Operation completed");
}
