//! Market data event types
//!
//! Consolidated market data types from data and trading_engine crates.

use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::fmt;

use crate::error::ErrorCategory;
use super::{Price, Quantity};

// =============================================================================
// MARKET DATA EVENT TYPES (Consolidated from data and trading_engine crates)
// =============================================================================

/// Market data event types - CANONICAL DEFINITION
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MarketDataEvent {
    /// Quote update (bid/ask)
    Quote(QuoteEvent),
    /// Trade execution
    Trade(TradeEvent),
    /// Aggregate trade data
    Aggregate(Aggregate),
    /// Bar/candle data
    Bar(BarEvent),
    /// Level 2 market data update
    Level2(Level2Update),
    /// Market status update
    Status(MarketStatus),
    /// Connection status updates
    ConnectionStatus(ConnectionEvent),
    /// Error events with details
    Error(ErrorEvent),
    /// Order book update
    OrderBook(OrderBookEvent),
    /// Level 2 order book snapshot
    OrderBookL2Snapshot(OrderBookSnapshot),
    /// Level 2 order book incremental update
    OrderBookL2Update(OrderBookUpdate),
}

/// Quote event structure - CANONICAL DEFINITION
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct QuoteEvent {
    /// Symbol
    pub symbol: String,
    /// Bid price
    pub bid: Option<Decimal>,
    /// Ask price
    pub ask: Option<Decimal>,
    /// Bid size
    pub bid_size: Option<Decimal>,
    /// Ask size
    pub ask_size: Option<Decimal>,
    /// Exchange
    pub exchange: Option<String>,
    /// Bid exchange
    pub bid_exchange: Option<String>,
    /// Ask exchange
    pub ask_exchange: Option<String>,
    /// Quote conditions
    pub conditions: Vec<String>,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
    /// Sequence number
    pub sequence: u64,
}

impl QuoteEvent {
    /// Create a new quote event
    #[must_use]
    pub const fn new(symbol: String, timestamp: DateTime<Utc>) -> Self {
        Self {
            symbol,
            bid: None,
            ask: None,
            bid_size: None,
            ask_size: None,
            exchange: None,
            bid_exchange: None,
            ask_exchange: None,
            conditions: Vec::new(),
            timestamp,
            sequence: 0,
        }
    }

    /// Set bid price and size
    pub const fn with_bid(mut self, price: Decimal, size: Decimal) -> Self {
        self.bid = Some(price);
        self.bid_size = Some(size);
        self
    }

    /// Set ask price and size
    pub const fn with_ask(mut self, price: Decimal, size: Decimal) -> Self {
        self.ask = Some(price);
        self.ask_size = Some(size);
        self
    }

    /// Set exchange
    pub fn with_exchange<S: Into<String>>(mut self, exchange: S) -> Self {
        self.exchange = Some(exchange.into());
        self
    }

    /// Set bid exchange
    pub fn with_bid_exchange<S: Into<String>>(mut self, exchange: S) -> Self {
        self.bid_exchange = Some(exchange.into());
        self
    }

    /// Set ask exchange
    pub fn with_ask_exchange<S: Into<String>>(mut self, exchange: S) -> Self {
        self.ask_exchange = Some(exchange.into());
        self
    }

    /// Add quote condition
    pub fn with_condition<S: Into<String>>(mut self, condition: S) -> Self {
        self.conditions.push(condition.into());
        self
    }

    /// Set sequence number
    pub const fn with_sequence(mut self, sequence: u64) -> Self {
        self.sequence = sequence;
        self
    }

    /// Get mid price
    pub fn mid_price(&self) -> Option<Decimal> {
        match (self.bid, self.ask) {
            (Some(bid), Some(ask)) => {
                let sum = bid.checked_add(ask)?;
                let two = Decimal::from(2);
                sum.checked_div(two)
            },
            _ => None,
        }
    }

    /// Get spread
    pub fn spread(&self) -> Option<Decimal> {
        match (self.bid, self.ask) {
            (Some(bid), Some(ask)) => ask.checked_sub(bid),
            _ => None,
        }
    }
}

/// Trade event structure - CANONICAL DEFINITION
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TradeEvent {
    /// Symbol
    pub symbol: String,
    /// Trade price
    pub price: Decimal,
    /// Trade size
    pub size: Decimal,
    /// Trade ID
    pub trade_id: Option<String>,
    /// Exchange
    pub exchange: Option<String>,
    /// Trade conditions
    pub conditions: Vec<String>,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
    /// Sequence number
    pub sequence: u64,
}

impl TradeEvent {
    /// Create a new trade event
    #[must_use]
    pub const fn new(
        symbol: String,
        price: Decimal,
        size: Decimal,
        timestamp: DateTime<Utc>,
    ) -> Self {
        Self {
            symbol,
            price,
            size,
            trade_id: None,
            exchange: None,
            conditions: Vec::new(),
            timestamp,
            sequence: 0,
        }
    }

    /// Set trade ID
    pub fn with_trade_id<S: Into<String>>(mut self, trade_id: S) -> Self {
        self.trade_id = Some(trade_id.into());
        self
    }

    /// Set exchange
    pub fn with_exchange<S: Into<String>>(mut self, exchange: S) -> Self {
        self.exchange = Some(exchange.into());
        self
    }

    /// Add trade condition
    pub fn with_condition<S: Into<String>>(mut self, condition: S) -> Self {
        self.conditions.push(condition.into());
        self
    }

    /// Set sequence number
    pub const fn with_sequence(mut self, sequence: u64) -> Self {
        self.sequence = sequence;
        self
    }

    /// Get notional value
    pub fn notional_value(&self) -> Decimal {
        self.price.checked_mul(self.size).unwrap_or(Decimal::ZERO)
    }
}

/// Aggregate trade data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Aggregate {
    /// Symbol
    pub symbol: String,
    /// Open price
    pub open: Decimal,
    /// High price
    pub high: Decimal,
    /// Low price
    pub low: Decimal,
    /// Close price
    pub close: Decimal,
    /// Volume
    pub volume: Decimal,
    /// Volume weighted average price
    pub vwap: Option<Decimal>,
    /// Start timestamp
    pub start_timestamp: DateTime<Utc>,
    /// End timestamp
    pub end_timestamp: DateTime<Utc>,
}

/// Bar/candle event structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BarEvent {
    /// Symbol
    pub symbol: String,
    /// Open price
    pub open: Decimal,
    /// High price
    pub high: Decimal,
    /// Low price
    pub low: Decimal,
    /// Close price
    pub close: Decimal,
    /// Volume
    pub volume: Decimal,
    /// Volume weighted average price
    pub vwap: Option<Decimal>,
    /// Start timestamp
    pub start_timestamp: DateTime<Utc>,
    /// End timestamp
    pub end_timestamp: DateTime<Utc>,
    /// Timeframe (e.g., "1m", "5m", "1h")
    pub timeframe: String,
}

/// Level 2 market data update
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Level2Update {
    /// Symbol
    pub symbol: String,
    /// Bid levels
    pub bids: Vec<PriceLevel>,
    /// Ask levels
    pub asks: Vec<PriceLevel>,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
}

/// Price level for order book
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriceLevel {
    /// Price
    pub price: Decimal,
    /// Size at this price level
    pub size: Decimal,
}

/// Order book snapshot from providers
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderBookSnapshot {
    /// Symbol
    pub symbol: String,
    /// Bid levels (price, size) sorted by price descending
    pub bids: Vec<PriceLevel>,
    /// Ask levels (price, size) sorted by price ascending
    pub asks: Vec<PriceLevel>,
    /// Exchange
    pub exchange: String,
    /// Timestamp of snapshot
    pub timestamp: DateTime<Utc>,
    /// Sequence number
    pub sequence: u64,
}

/// Incremental order book update from providers
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderBookUpdate {
    /// Symbol
    pub symbol: String,
    /// Changes to bid levels
    pub bid_changes: Vec<PriceLevelChange>,
    /// Changes to ask levels
    pub ask_changes: Vec<PriceLevelChange>,
    /// Exchange
    pub exchange: String,
    /// Timestamp of update
    pub timestamp: DateTime<Utc>,
    /// Sequence number
    pub sequence: u64,
}

/// Change to a price level
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriceLevelChange {
    /// Price level being modified
    pub price: Decimal,
    /// New size (0 = remove level)
    pub size: Decimal,
    /// Type of change
    pub change_type: PriceLevelChangeType,
    /// Side (bid or ask)
    pub side: OrderBookSide,
}

/// Type of price level change
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum PriceLevelChangeType {
    /// Add new price level
    Add,
    /// Update existing price level
    Update,
    /// Remove price level
    Delete,
}

/// Order book side
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum OrderBookSide {
    /// Bid side
    Bid,
    /// Ask side
    Ask,
}

/// Market status information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketStatus {
    /// Market
    pub market: String,
    /// Status (open, closed, `early_hours`, etc.)
    pub status: String,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
}

/// Connection event for status updates
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectionEvent {
    /// Provider name
    pub provider: String,
    /// Connection status
    pub status: ConnectionStatus,
    /// Optional message
    pub message: Option<String>,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
}

/// Connection status enumeration
///
/// Connection status for data providers and brokers
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "database", derive(sqlx::Type))]
#[cfg_attr(
    feature = "database",
    sqlx(type_name = "connection_status", rename_all = "snake_case")
)]
pub enum ConnectionStatus {
    /// Successfully connected and operational
    Connected,
    /// Disconnected from the service
    Disconnected,
    /// Currently attempting to reconnect
    Reconnecting,
}

/// Error event structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorEvent {
    /// Provider name
    pub provider: String,
    /// Error message
    pub message: String,
    /// Error category
    pub category: ErrorCategory,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
}

// ErrorCategory is imported from crate::error as CommonErrorCategory

/// Order book event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrderBookEvent {
    /// Symbol
    pub symbol: String,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
    /// Bid levels
    pub bids: Vec<(Price, Quantity)>,
    /// Ask levels
    pub asks: Vec<(Price, Quantity)>,
}

/// Data types for subscription
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DataType {
    /// Real-time quotes
    Quotes,
    /// Real-time trades
    Trades,
    /// Aggregate/minute bars
    Aggregates,
    /// Level 2 order book
    Level2,
    /// Market status
    Status,
    /// Historical bars/aggregates
    Bars,
    /// Order book data
    OrderBook,
    /// Volume data
    Volume,
}

/// Market data subscription request
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Subscription {
    /// Symbols to subscribe to
    pub symbols: Vec<String>,
    /// Data types to subscribe to
    pub data_types: Vec<DataType>,
    /// Exchange filter (optional)
    pub exchanges: Vec<String>,
}

impl MarketDataEvent {
    /// Get the symbol for any market data event
    pub fn symbol(&self) -> &str {
        match self {
            MarketDataEvent::Quote(q) => &q.symbol,
            MarketDataEvent::Trade(t) => &t.symbol,
            MarketDataEvent::Aggregate(a) => &a.symbol,
            MarketDataEvent::Bar(b) => &b.symbol,
            MarketDataEvent::Level2(l) => &l.symbol,
            MarketDataEvent::Status(s) => &s.market,
            MarketDataEvent::ConnectionStatus(_) => "",
            MarketDataEvent::Error(_) => "",
            MarketDataEvent::OrderBook(o) => &o.symbol,
            MarketDataEvent::OrderBookL2Snapshot(s) => &s.symbol,
            MarketDataEvent::OrderBookL2Update(u) => &u.symbol,
        }
    }

    /// Get the timestamp for any market data event
    pub const fn timestamp(&self) -> Option<DateTime<Utc>> {
        match self {
            MarketDataEvent::Quote(q) => Some(q.timestamp),
            MarketDataEvent::Trade(t) => Some(t.timestamp),
            MarketDataEvent::Aggregate(a) => Some(a.end_timestamp),
            MarketDataEvent::Bar(b) => Some(b.end_timestamp),
            MarketDataEvent::Level2(l) => Some(l.timestamp),
            MarketDataEvent::Status(s) => Some(s.timestamp),
            MarketDataEvent::ConnectionStatus(c) => Some(c.timestamp),
            MarketDataEvent::Error(e) => Some(e.timestamp),
            MarketDataEvent::OrderBook(o) => Some(o.timestamp),
            MarketDataEvent::OrderBookL2Snapshot(s) => Some(s.timestamp),
            MarketDataEvent::OrderBookL2Update(u) => Some(u.timestamp),
        }
    }
}

/// Connection information for services
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectionInfo {
    /// Host address
    pub host: String,
    /// Port number
    pub port: u16,
    /// Whether TLS is enabled
    pub tls: bool,
    /// Connection timeout in milliseconds
    pub timeout_ms: u64,
}

impl ConnectionInfo {
    /// Create new connection info
    pub fn new<S: Into<String>>(host: S, port: u16) -> Self {
        Self {
            host: host.into(),
            port,
            tls: false,
            timeout_ms: 5000,
        }
    }

    /// Enable TLS
    pub const fn with_tls(mut self) -> Self {
        self.tls = true;
        self
    }

    /// Set timeout
    pub const fn with_timeout(mut self, timeout_ms: u64) -> Self {
        self.timeout_ms = timeout_ms;
        self
    }

    /// Get connection URL
    pub fn url(&self) -> String {
        let scheme = if self.tls { "https" } else { "http" };
        format!("{}://{}:{}", scheme, self.host, self.port)
    }
}

impl fmt::Display for ConnectionInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.url())
    }
}

/// Resource limits for services
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ResourceLimits {
    /// Maximum memory usage in bytes
    pub max_memory_bytes: Option<u64>,
    /// Maximum CPU usage as percentage (0-100)
    pub max_cpu_percent: Option<f64>,
    /// Maximum number of open file descriptors
    pub max_file_descriptors: Option<u32>,
    /// Maximum number of network connections
    pub max_connections: Option<u32>,
}
