//! # Trading Events Module
//!
//! Defines the structure and types for trading events that can be streamed
//! through the trading service event system.

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Core trading event structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TradingEvent {
    /// Unique event identifier
    pub id: String,
    /// Type of trading event
    pub event_type: TradingEventType,
    /// When the event occurred
    pub timestamp: DateTime<Utc>,
    /// Source service or component
    pub source: String,
    /// Correlation ID for tracking related events
    pub correlation_id: Option<String>,
    /// Event severity level
    pub severity: EventSeverity,
    /// Event payload data
    pub payload: String,
    /// Additional metadata
    pub metadata: HashMap<String, String>,
}

impl TradingEvent {
    /// Create a new trading event
    pub fn new(event_type: TradingEventType, correlation_id: String, payload: String) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            event_type,
            timestamp: Utc::now(),
            source: "trading_service".to_string(),
            correlation_id: Some(correlation_id),
            severity: EventSeverity::Info,
            payload,
            metadata: HashMap::new(),
        }
    }

    /// Create a new trading event with custom source
    pub fn with_source(
        event_type: TradingEventType,
        source: String,
        correlation_id: String,
        payload: String,
    ) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            event_type,
            timestamp: Utc::now(),
            source,
            correlation_id: Some(correlation_id),
            severity: EventSeverity::Info,
            payload,
            metadata: HashMap::new(),
        }
    }

    /// Set the event severity
    pub fn with_severity(mut self, severity: EventSeverity) -> Self {
        self.severity = severity;
        self
    }

    /// Add metadata to the event
    pub fn add_metadata(&mut self, key: String, value: String) {
        self.metadata.insert(key, value);
    }

    /// Get metadata value by key
    pub fn get_metadata(&self, key: &str) -> Option<&String> {
        self.metadata.get(key)
    }

    /// Check if event matches a specific correlation ID
    pub fn matches_correlation_id(&self, correlation_id: &str) -> bool {
        self.correlation_id
            .as_ref()
            .map(|id| id == correlation_id)
            .unwrap_or(false)
    }

    /// Convert timestamp to RFC3339 string
    pub fn timestamp_rfc3339(&self) -> String {
        self.timestamp.to_rfc3339()
    }

    /// Get event age in milliseconds
    pub fn age_millis(&self) -> i64 {
        let now = Utc::now();
        (now - self.timestamp).num_milliseconds()
    }

    /// Get the event type
    pub fn event_type(&self) -> TradingEventType {
        self.event_type
    }

    /// Estimate the size of this event in bytes
    pub fn estimated_size(&self) -> usize {
        std::mem::size_of::<Self>()
            + self.id.len()
            + self.source.len()
            + self.correlation_id.as_ref().map(|s| s.len()).unwrap_or(0)
            + self.payload.len()
            + self
                .metadata
                .iter()
                .map(|(k, v)| k.len() + v.len())
                .sum::<usize>()
    }

    /// Check if this is an order-related event
    pub fn is_order_event(&self) -> bool {
        self.event_type.is_order_event()
    }

    /// Check if this is a position-related event
    pub fn is_position_event(&self) -> bool {
        self.event_type.is_position_event()
    }

    /// Check if this is an execution-related event
    pub fn is_execution_event(&self) -> bool {
        self.event_type.is_execution_event()
    }

    /// Check if this event matches the given account ID
    pub fn matches_account(&self, account_id: &str) -> bool {
        self.metadata
            .get("account_id")
            .map(|id| id == account_id)
            .unwrap_or(false)
    }
}

/// Types of trading events that can be streamed
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum TradingEventType {
    // Order Events
    OrderSubmitted,
    OrderAccepted,
    OrderRejected,
    OrderCancelled,
    OrderExpired,
    OrderModified,

    // Fill Events
    OrderFilled,
    PartialFill,

    // Position Events
    PositionOpened,
    PositionClosed,
    PositionModified,

    // Risk Events
    RiskLimitBreached,
    RiskWarning,
    EmergencyStop,

    // Market Data Events
    PriceUpdate,
    VolumeUpdate,
    OrderBookUpdate,

    // System Events
    ServiceStarted,
    ServiceStopped,
    ConnectionEstablished,
    ConnectionLost,
    LatencyAlert,

    // ML Events
    ModelPrediction,
    SignalGenerated,
    ModelRetrained,

    // Account Events
    BalanceUpdate,
    MarginCall,
    AccountSuspended,
}

impl TradingEventType {
    /// Get string representation of the event type
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::OrderSubmitted => "order_submitted",
            Self::OrderAccepted => "order_accepted",
            Self::OrderRejected => "order_rejected",
            Self::OrderCancelled => "order_cancelled",
            Self::OrderExpired => "order_expired",
            Self::OrderModified => "order_modified",
            Self::OrderFilled => "order_filled",
            Self::PartialFill => "partial_fill",
            Self::PositionOpened => "position_opened",
            Self::PositionClosed => "position_closed",
            Self::PositionModified => "position_modified",
            Self::RiskLimitBreached => "risk_limit_breached",
            Self::RiskWarning => "risk_warning",
            Self::EmergencyStop => "emergency_stop",
            Self::PriceUpdate => "price_update",
            Self::VolumeUpdate => "volume_update",
            Self::OrderBookUpdate => "order_book_update",
            Self::ServiceStarted => "service_started",
            Self::ServiceStopped => "service_stopped",
            Self::ConnectionEstablished => "connection_established",
            Self::ConnectionLost => "connection_lost",
            Self::LatencyAlert => "latency_alert",
            Self::ModelPrediction => "model_prediction",
            Self::SignalGenerated => "signal_generated",
            Self::ModelRetrained => "model_retrained",
            Self::BalanceUpdate => "balance_update",
            Self::MarginCall => "margin_call",
            Self::AccountSuspended => "account_suspended",
        }
    }

    /// Check if this is an order-related event type
    pub fn is_order_event(&self) -> bool {
        matches!(
            self,
            Self::OrderSubmitted
                | Self::OrderAccepted
                | Self::OrderRejected
                | Self::OrderCancelled
                | Self::OrderExpired
                | Self::OrderModified
                | Self::OrderFilled
                | Self::PartialFill
        )
    }

    /// Check if this is a position-related event type
    pub fn is_position_event(&self) -> bool {
        matches!(
            self,
            Self::PositionOpened | Self::PositionClosed | Self::PositionModified
        )
    }

    /// Check if this is an execution-related event type
    pub fn is_execution_event(&self) -> bool {
        matches!(self, Self::OrderFilled | Self::PartialFill)
    }

    /// Check if this is a market data event type
    pub fn is_market_data_event(&self) -> bool {
        matches!(
            self,
            Self::PriceUpdate | Self::VolumeUpdate | Self::OrderBookUpdate
        )
    }

    /// Parse event type from string
    pub fn from_str(s: &str) -> Option<Self> {
        match s {
            "order_submitted" => Some(Self::OrderSubmitted),
            "order_accepted" => Some(Self::OrderAccepted),
            "order_rejected" => Some(Self::OrderRejected),
            "order_cancelled" => Some(Self::OrderCancelled),
            "order_expired" => Some(Self::OrderExpired),
            "order_modified" => Some(Self::OrderModified),
            "order_filled" => Some(Self::OrderFilled),
            "partial_fill" => Some(Self::PartialFill),
            "position_opened" => Some(Self::PositionOpened),
            "position_closed" => Some(Self::PositionClosed),
            "position_modified" => Some(Self::PositionModified),
            "risk_limit_breached" => Some(Self::RiskLimitBreached),
            "risk_warning" => Some(Self::RiskWarning),
            "emergency_stop" => Some(Self::EmergencyStop),
            "price_update" => Some(Self::PriceUpdate),
            "volume_update" => Some(Self::VolumeUpdate),
            "order_book_update" => Some(Self::OrderBookUpdate),
            "service_started" => Some(Self::ServiceStarted),
            "service_stopped" => Some(Self::ServiceStopped),
            "connection_established" => Some(Self::ConnectionEstablished),
            "connection_lost" => Some(Self::ConnectionLost),
            "latency_alert" => Some(Self::LatencyAlert),
            "model_prediction" => Some(Self::ModelPrediction),
            "signal_generated" => Some(Self::SignalGenerated),
            "model_retrained" => Some(Self::ModelRetrained),
            "balance_update" => Some(Self::BalanceUpdate),
            "margin_call" => Some(Self::MarginCall),
            "account_suspended" => Some(Self::AccountSuspended),
            _ => None,
        }
    }

    /// Check if this is a risk-related event
    pub fn is_risk_event(&self) -> bool {
        matches!(
            self,
            Self::RiskLimitBreached | Self::RiskWarning | Self::EmergencyStop
        )
    }

    /// Check if this is a system event
    pub fn is_system_event(&self) -> bool {
        matches!(
            self,
            Self::ServiceStarted
                | Self::ServiceStopped
                | Self::ConnectionEstablished
                | Self::ConnectionLost
                | Self::LatencyAlert
        )
    }

    /// Get event category
    pub fn category(&self) -> EventCategory {
        match self {
            Self::OrderSubmitted
            | Self::OrderAccepted
            | Self::OrderRejected
            | Self::OrderCancelled
            | Self::OrderExpired
            | Self::OrderModified
            | Self::OrderFilled
            | Self::PartialFill => EventCategory::Order,

            Self::PositionOpened | Self::PositionClosed | Self::PositionModified => {
                EventCategory::Position
            },

            Self::RiskLimitBreached | Self::RiskWarning | Self::EmergencyStop => {
                EventCategory::Risk
            },

            Self::PriceUpdate | Self::VolumeUpdate | Self::OrderBookUpdate => {
                EventCategory::MarketData
            },

            Self::ServiceStarted
            | Self::ServiceStopped
            | Self::ConnectionEstablished
            | Self::ConnectionLost
            | Self::LatencyAlert => EventCategory::System,

            Self::ModelPrediction | Self::SignalGenerated | Self::ModelRetrained => {
                EventCategory::ML
            },

            Self::BalanceUpdate | Self::MarginCall | Self::AccountSuspended => {
                EventCategory::Account
            },
        }
    }
}

/// Event categories for grouping related event types
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EventCategory {
    Order,
    Position,
    Risk,
    MarketData,
    System,
    ML,
    Account,
}

impl EventCategory {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Order => "order",
            Self::Position => "position",
            Self::Risk => "risk",
            Self::MarketData => "market_data",
            Self::System => "system",
            Self::ML => "ml",
            Self::Account => "account",
        }
    }
}

/// Event severity levels
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum EventSeverity {
    Debug = 0,
    Info = 1,
    Warning = 2,
    Error = 3,
    Critical = 4,
}

impl EventSeverity {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Debug => "debug",
            Self::Info => "info",
            Self::Warning => "warning",
            Self::Error => "error",
            Self::Critical => "critical",
        }
    }

    pub fn from_str(s: &str) -> Option<Self> {
        match s.to_lowercase().as_str() {
            "debug" => Some(Self::Debug),
            "info" => Some(Self::Info),
            "warning" | "warn" => Some(Self::Warning),
            "error" => Some(Self::Error),
            "critical" | "crit" => Some(Self::Critical),
            _ => None,
        }
    }
}

/// Helper functions for creating common trading events
impl TradingEvent {
    /// Create an order submitted event
    pub fn order_submitted(
        order_id: String,
        symbol: String,
        side: String,
        quantity: f64,
        price: f64,
    ) -> Self {
        let payload = serde_json::json!({
            "order_id": order_id,
            "symbol": symbol,
            "side": side,
            "quantity": quantity,
            "price": price
        })
        .to_string();

        Self::new(TradingEventType::OrderSubmitted, order_id, payload)
    }

    /// Create an order filled event
    pub fn order_filled(
        order_id: String,
        symbol: String,
        filled_qty: f64,
        fill_price: f64,
    ) -> Self {
        let payload = serde_json::json!({
            "order_id": order_id,
            "symbol": symbol,
            "filled_quantity": filled_qty,
            "fill_price": fill_price,
            "fill_time": Utc::now().to_rfc3339()
        })
        .to_string();

        Self::new(TradingEventType::OrderFilled, order_id, payload)
    }

    /// Create an order rejected event
    pub fn order_rejected(order_id: String, reason: String) -> Self {
        let payload = serde_json::json!({
            "order_id": order_id,
            "rejection_reason": reason
        })
        .to_string();

        Self::new(TradingEventType::OrderRejected, order_id, payload)
            .with_severity(EventSeverity::Warning)
    }

    /// Create a risk warning event
    pub fn risk_warning(message: String, risk_type: String) -> Self {
        let payload = serde_json::json!({
            "message": message,
            "risk_type": risk_type,
            "timestamp": Utc::now().to_rfc3339()
        })
        .to_string();

        Self::new(
            TradingEventType::RiskWarning,
            "risk_system".to_string(),
            payload,
        )
        .with_severity(EventSeverity::Warning)
    }

    /// Create an emergency stop event
    pub fn emergency_stop(reason: String) -> Self {
        let payload = serde_json::json!({
            "reason": reason,
            "stop_time": Utc::now().to_rfc3339()
        })
        .to_string();

        Self::new(
            TradingEventType::EmergencyStop,
            "risk_system".to_string(),
            payload,
        )
        .with_severity(EventSeverity::Critical)
    }

    /// Create a price update event
    pub fn price_update(symbol: String, price: f64, volume: f64) -> Self {
        let payload = serde_json::json!({
            "symbol": symbol,
            "price": price,
            "volume": volume,
            "timestamp": Utc::now().to_rfc3339()
        })
        .to_string();

        Self::new(TradingEventType::PriceUpdate, symbol, payload)
    }

    /// Create a latency alert event
    pub fn latency_alert(component: String, latency_ms: u64, threshold_ms: u64) -> Self {
        let payload = serde_json::json!({
            "component": component,
            "latency_ms": latency_ms,
            "threshold_ms": threshold_ms,
            "timestamp": Utc::now().to_rfc3339()
        })
        .to_string();

        Self::new(TradingEventType::LatencyAlert, component, payload)
            .with_severity(EventSeverity::Warning)
    }
}

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

    #[test]
    fn test_trading_event_creation() {
        let event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order123".to_string(),
            "test payload".to_string(),
        );

        assert_eq!(event.event_type, TradingEventType::OrderSubmitted);
        assert_eq!(event.correlation_id, Some("order123".to_string()));
        assert_eq!(event.payload, "test payload");
        assert_eq!(event.severity, EventSeverity::Info);
        assert!(!event.id.is_empty());
    }

    #[test]
    fn test_event_type_string_conversion() {
        let event_type = TradingEventType::OrderSubmitted;
        assert_eq!(event_type.as_str(), "order_submitted");
        assert_eq!(
            TradingEventType::from_str("order_submitted"),
            Some(event_type)
        );
    }

    #[test]
    fn test_event_type_categories() {
        assert!(TradingEventType::OrderSubmitted.is_order_event());
        assert!(TradingEventType::RiskWarning.is_risk_event());
        assert!(TradingEventType::PriceUpdate.is_market_data_event());
        assert!(TradingEventType::ServiceStarted.is_system_event());

        assert_eq!(
            TradingEventType::OrderSubmitted.category(),
            EventCategory::Order
        );
        assert_eq!(
            TradingEventType::RiskWarning.category(),
            EventCategory::Risk
        );
    }

    #[test]
    fn test_event_severity() {
        assert!(EventSeverity::Critical > EventSeverity::Warning);
        assert!(EventSeverity::Warning > EventSeverity::Info);

        assert_eq!(EventSeverity::Info.as_str(), "info");
        assert_eq!(
            EventSeverity::from_str("warning"),
            Some(EventSeverity::Warning)
        );
    }

    #[test]
    fn test_event_metadata() {
        use crate::test_utils::TestConfig;

        let test_config = TestConfig::new();
        let test_symbol = test_config.primary_symbol();

        let mut event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order123".to_string(),
            "test".to_string(),
        );

        event.add_metadata("symbol".to_string(), test_symbol.to_string());
        assert_eq!(event.get_metadata("symbol"), Some(&test_symbol.to_string()));
    }

    #[test]
    fn test_helper_event_creation() {
        use crate::test_utils::TestFixtures;

        let fixtures = TestFixtures::new();
        let test_symbol = fixtures.config.primary_symbol();
        let (price, _) = fixtures.test_prices(test_symbol);
        let (quantity, _) = fixtures.test_quantities();

        let event = TradingEvent::order_submitted(
            "order123".to_string(),
            test_symbol.to_string(),
            "BUY".to_string(),
            quantity,
            price,
        );

        assert_eq!(event.event_type, TradingEventType::OrderSubmitted);
        assert!(event.payload.contains(test_symbol));
        assert!(event.payload.contains("order123"));
    }

    #[test]
    fn test_correlation_id_matching() {
        let event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order123".to_string(),
            "test".to_string(),
        );

        assert!(event.matches_correlation_id("order123"));
        assert!(!event.matches_correlation_id("order456"));
    }

    #[test]
    fn test_event_age() {
        let event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order123".to_string(),
            "test".to_string(),
        );

        let age = event.age_millis();
        assert!(age >= 0);
        assert!(age < 1000); // Should be very recent
    }
}
