//! Type-Safe Event Definitions for Trading System
//!
//! This module defines all event types used in the high-frequency trading system
//! with comprehensive serialization, validation, and metadata support.

use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::collections::HashMap;

use crate::timing::HardwareTimestamp;
use rust_decimal::Decimal;

/// Core trading event types with comprehensive metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
/// Core trading events with comprehensive metadata
///
/// Type-safe event definitions for all trading system events including
/// order lifecycle, position updates, risk alerts, and system events.
pub enum TradingEvent {
    /// Order submission event
    OrderSubmitted {
        order_id: String,
        symbol: String,
        quantity: Decimal,
        price: Decimal,
        timestamp: HardwareTimestamp,
        #[serde(skip_serializing_if = "Option::is_none")]
        sequence_number: Option<u64>,
        #[serde(skip_serializing_if = "Option::is_none")]
        metadata: Option<JsonValue>,
    },

    /// Order execution event
    OrderExecuted {
        trade_id: String,
        symbol: String,
        quantity: Decimal,
        price: Decimal,
        timestamp: HardwareTimestamp,
        #[serde(skip_serializing_if = "Option::is_none")]
        sequence_number: Option<u64>,
        #[serde(skip_serializing_if = "Option::is_none")]
        metadata: Option<JsonValue>,
    },

    /// Order cancellation event
    OrderCancelled {
        order_id: String,
        symbol: String,
        timestamp: HardwareTimestamp,
        reason: String,
        #[serde(skip_serializing_if = "Option::is_none")]
        sequence_number: Option<u64>,
        #[serde(skip_serializing_if = "Option::is_none")]
        metadata: Option<JsonValue>,
    },

    /// Position update event
    PositionUpdated {
        symbol: String,
        quantity: Decimal,
        avg_price: Decimal,
        unrealized_pnl: Decimal,
        timestamp: HardwareTimestamp,
        #[serde(skip_serializing_if = "Option::is_none")]
        sequence_number: Option<u64>,
        #[serde(skip_serializing_if = "Option::is_none")]
        metadata: Option<JsonValue>,
    },

    /// Risk alert event
    RiskAlert {
        alert_type: RiskAlertType,
        symbol: Option<String>,
        message: String,
        severity: AlertSeverity,
        timestamp: HardwareTimestamp,
        #[serde(skip_serializing_if = "Option::is_none")]
        sequence_number: Option<u64>,
        #[serde(skip_serializing_if = "Option::is_none")]
        metadata: Option<JsonValue>,
    },

    /// System event
    SystemEvent {
        event_type: SystemEventType,
        message: String,
        level: EventLevel,
        timestamp: HardwareTimestamp,
        #[serde(skip_serializing_if = "Option::is_none")]
        sequence_number: Option<u64>,
        #[serde(skip_serializing_if = "Option::is_none")]
        metadata: Option<JsonValue>,
    },
}

impl TradingEvent {
    /// Get the event type as a string
    pub const fn event_type(&self) -> &'static str {
        match self {
            TradingEvent::OrderSubmitted { .. } => "order_submitted",
            TradingEvent::OrderExecuted { .. } => "order_executed",
            TradingEvent::OrderCancelled { .. } => "order_cancelled",
            TradingEvent::PositionUpdated { .. } => "position_updated",
            TradingEvent::RiskAlert { .. } => "risk_alert",
            TradingEvent::SystemEvent { .. } => "system_event",
        }
    }

    /// Get the event timestamp
    pub const fn timestamp(&self) -> HardwareTimestamp {
        match self {
            TradingEvent::OrderSubmitted { timestamp, .. } => *timestamp,
            TradingEvent::OrderExecuted { timestamp, .. } => *timestamp,
            TradingEvent::OrderCancelled { timestamp, .. } => *timestamp,
            TradingEvent::PositionUpdated { timestamp, .. } => *timestamp,
            TradingEvent::RiskAlert { timestamp, .. } => *timestamp,
            TradingEvent::SystemEvent { timestamp, .. } => *timestamp,
        }
    }

    /// Get the sequence number if present
    pub const fn sequence_number(&self) -> Option<u64> {
        match self {
            TradingEvent::OrderSubmitted {
                sequence_number, ..
            } => *sequence_number,
            TradingEvent::OrderExecuted {
                sequence_number, ..
            } => *sequence_number,
            TradingEvent::OrderCancelled {
                sequence_number, ..
            } => *sequence_number,
            TradingEvent::PositionUpdated {
                sequence_number, ..
            } => *sequence_number,
            TradingEvent::RiskAlert {
                sequence_number, ..
            } => *sequence_number,
            TradingEvent::SystemEvent {
                sequence_number, ..
            } => *sequence_number,
        }
    }

    /// Set the sequence number
    pub fn set_sequence_number(&mut self, seq: u64) {
        match self {
            TradingEvent::OrderSubmitted {
                sequence_number, ..
            } => *sequence_number = Some(seq),
            TradingEvent::OrderExecuted {
                sequence_number, ..
            } => *sequence_number = Some(seq),
            TradingEvent::OrderCancelled {
                sequence_number, ..
            } => *sequence_number = Some(seq),
            TradingEvent::PositionUpdated {
                sequence_number, ..
            } => *sequence_number = Some(seq),
            TradingEvent::RiskAlert {
                sequence_number, ..
            } => *sequence_number = Some(seq),
            TradingEvent::SystemEvent {
                sequence_number, ..
            } => *sequence_number = Some(seq),
        }
    }

    /// Get the metadata if present
    pub const fn metadata(&self) -> Option<&JsonValue> {
        match self {
            TradingEvent::OrderSubmitted { metadata, .. } => metadata.as_ref(),
            TradingEvent::OrderExecuted { metadata, .. } => metadata.as_ref(),
            TradingEvent::OrderCancelled { metadata, .. } => metadata.as_ref(),
            TradingEvent::PositionUpdated { metadata, .. } => metadata.as_ref(),
            TradingEvent::RiskAlert { metadata, .. } => metadata.as_ref(),
            TradingEvent::SystemEvent { metadata, .. } => metadata.as_ref(),
        }
    }

    /// Set metadata
    pub fn set_metadata(&mut self, metadata: JsonValue) {
        match self {
            TradingEvent::OrderSubmitted { metadata: meta, .. } => *meta = Some(metadata),
            TradingEvent::OrderExecuted { metadata: meta, .. } => *meta = Some(metadata),
            TradingEvent::OrderCancelled { metadata: meta, .. } => *meta = Some(metadata),
            TradingEvent::PositionUpdated { metadata: meta, .. } => *meta = Some(metadata),
            TradingEvent::RiskAlert { metadata: meta, .. } => *meta = Some(metadata),
            TradingEvent::SystemEvent { metadata: meta, .. } => *meta = Some(metadata),
        }
    }

    /// Get the event level/severity
    pub const fn level(&self) -> EventLevel {
        match self {
            TradingEvent::OrderSubmitted { .. } => EventLevel::Info,
            TradingEvent::OrderExecuted { .. } => EventLevel::Info,
            TradingEvent::OrderCancelled { .. } => EventLevel::Warning,
            TradingEvent::PositionUpdated { .. } => EventLevel::Info,
            TradingEvent::RiskAlert { severity, .. } => match severity {
                AlertSeverity::Low => EventLevel::Info,
                AlertSeverity::Medium => EventLevel::Warning,
                AlertSeverity::High => EventLevel::Error,
                AlertSeverity::Critical => EventLevel::Critical,
            },
            TradingEvent::SystemEvent { level, .. } => *level,
        }
    }

    /// Set the capture timestamp (when the event was captured by the system)
    pub fn set_capture_timestamp(&mut self, timestamp: HardwareTimestamp) {
        let capture_metadata = serde_json::json!({
            "capture_timestamp_ns": timestamp.nanos,
            "capture_source": timestamp.source
        });

        if let Some(existing) = self.metadata() {
            if let JsonValue::Object(mut map) = existing.clone() {
                map.insert("capture_info".to_owned(), capture_metadata);
                self.set_metadata(JsonValue::Object(map));
            }
        } else {
            let metadata = serde_json::json!({
                "capture_info": capture_metadata
            });
            self.set_metadata(metadata);
        }
    }

    /// Get the capture timestamp from metadata
    pub fn capture_timestamp(&self) -> Option<HardwareTimestamp> {
        self.metadata()?
            .get("capture_info")?
            .get("capture_timestamp_ns")?
            .as_u64()
            .map(|nanos| HardwareTimestamp {
                cycles: 0,
                nanos,
                source: crate::timing::TimingSource::RDTSC,
                validation_passed: true,
            })
    }

    /// Get the symbol associated with this event, if any
    pub fn symbol(&self) -> Option<&str> {
        match self {
            TradingEvent::OrderSubmitted { symbol, .. } => Some(symbol),
            TradingEvent::OrderExecuted { symbol, .. } => Some(symbol),
            TradingEvent::OrderCancelled { symbol, .. } => Some(symbol),
            TradingEvent::PositionUpdated { symbol, .. } => Some(symbol),
            TradingEvent::RiskAlert { symbol, .. } => symbol.as_deref(),
            TradingEvent::SystemEvent { .. } => None,
        }
    }

    /// Get a human-readable description of the event
    pub fn description(&self) -> String {
        match self {
            TradingEvent::OrderSubmitted {
                order_id,
                symbol,
                quantity,
                price,
                ..
            } => {
                format!(
                    "Order {} submitted: {} {} @ {}",
                    order_id, quantity, symbol, price
                )
            },
            TradingEvent::OrderExecuted {
                trade_id,
                symbol,
                quantity,
                price,
                ..
            } => {
                format!(
                    "Trade {} executed: {} {} @ {}",
                    trade_id, quantity, symbol, price
                )
            },
            TradingEvent::OrderCancelled {
                order_id,
                symbol,
                reason,
                ..
            } => {
                format!("Order {} cancelled for {}: {}", order_id, symbol, reason)
            },
            TradingEvent::PositionUpdated {
                symbol,
                quantity,
                avg_price,
                unrealized_pnl,
                ..
            } => {
                format!(
                    "Position updated for {}: {} @ {} (PnL: {})",
                    symbol, quantity, avg_price, unrealized_pnl
                )
            },
            TradingEvent::RiskAlert {
                alert_type,
                symbol,
                message,
                severity,
                ..
            } => {
                format!(
                    "Risk alert ({:?}): {} - {} [{}]",
                    severity,
                    alert_type,
                    message,
                    symbol.as_deref().unwrap_or("ALL")
                )
            },
            TradingEvent::SystemEvent {
                event_type,
                message,
                level,
                ..
            } => {
                format!("System event ({:?}): {:?} - {}", level, event_type, message)
            },
        }
    }

    /// Check if this event is critical and requires immediate attention
    pub const fn is_critical(&self) -> bool {
        matches!(self.level(), EventLevel::Critical | EventLevel::Error)
    }

    /// Get the estimated serialized size in bytes
    pub fn estimated_size(&self) -> usize {
        // Rough estimation for memory planning
        match self {
            TradingEvent::OrderSubmitted {
                order_id, symbol, ..
            } => 100 + order_id.len() + symbol.len(),
            TradingEvent::OrderExecuted {
                trade_id, symbol, ..
            } => 100 + trade_id.len() + symbol.len(),
            TradingEvent::OrderCancelled {
                order_id,
                symbol,
                reason,
                ..
            } => 100 + order_id.len() + symbol.len() + reason.len(),
            TradingEvent::PositionUpdated { symbol, .. } => 150 + symbol.len(),
            TradingEvent::RiskAlert {
                message, symbol, ..
            } => 120 + message.len() + symbol.as_ref().map(|s| s.len()).unwrap_or(0),
            TradingEvent::SystemEvent { message, .. } => 80 + message.len(),
        }
    }
}

/// Event severity levels
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
/// Event severity levels for logging and alerting
///
/// Hierarchical severity levels for event classification,
/// filtering, and prioritization in monitoring systems.
pub enum EventLevel {
    /// Debug information for development
    Debug,
    /// Informational events
    Info,
    /// Warning conditions that need attention
    Warning,
    /// Error conditions that affect functionality
    Error,
    /// Critical errors requiring immediate action
    Critical,
}

impl std::fmt::Display for EventLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            EventLevel::Debug => write!(f, "DEBUG"),
            EventLevel::Info => write!(f, "INFO"),
            EventLevel::Warning => write!(f, "WARNING"),
            EventLevel::Error => write!(f, "ERROR"),
            EventLevel::Critical => write!(f, "CRITICAL"),
        }
    }
}

/// Risk alert types
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
/// Types of risk management alerts
///
/// Classification of risk management alerts including position limits,
/// loss limits, volatility spikes, and custom risk rule violations.
pub enum RiskAlertType {
    /// Position size limit exceeded
    PositionSizeLimit,
    /// Daily loss limit approached
    DailyLossLimit,
    /// Drawdown limit exceeded
    DrawdownLimit,
    /// Market volatility spike
    VolatilitySpike,
    /// Liquidity constraint
    LiquidityConstraint,
    /// Custom risk rule violation
    CustomRule(String),
}

impl std::fmt::Display for RiskAlertType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RiskAlertType::PositionSizeLimit => write!(f, "Position Size Limit"),
            RiskAlertType::DailyLossLimit => write!(f, "Daily Loss Limit"),
            RiskAlertType::DrawdownLimit => write!(f, "Drawdown Limit"),
            RiskAlertType::VolatilitySpike => write!(f, "Volatility Spike"),
            RiskAlertType::LiquidityConstraint => write!(f, "Liquidity Constraint"),
            RiskAlertType::CustomRule(rule) => write!(f, "Custom Rule: {}", rule),
        }
    }
}

/// Alert severity levels
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// Alert severity levels for risk management
///
/// Graduated severity levels for risk alerts to prioritize
/// response and determine appropriate actions.
pub enum AlertSeverity {
    /// Low priority alert for monitoring
    Low,
    /// Medium priority requiring attention
    Medium,
    /// High priority requiring prompt action
    High,
    /// Critical priority requiring immediate action
    Critical,
}

/// System event types
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
/// Types of system infrastructure events
///
/// Classification of system events including startup/shutdown,
/// service connections, configuration changes, and performance alerts.
pub enum SystemEventType {
    /// System startup
    Startup,
    /// System shutdown
    Shutdown,
    /// Service connected
    ServiceConnected,
    /// Service disconnected
    ServiceDisconnected,
    /// Configuration change
    ConfigurationChange,
    /// Market data feed status
    MarketDataFeed,
    /// Database connection status
    DatabaseConnection,
    /// Memory usage alert
    MemoryUsage,
    /// Performance degradation
    PerformanceDegradation,
    /// Custom system event
    Custom(String),
}

/// Event sequence tracking
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
/// Event sequence tracking for ordering
///
/// Tracks event sequence numbers and creation timestamps
/// to ensure proper event ordering and detect missing events.
pub struct EventSequence {
    /// Unique sequence number for event ordering
    pub sequence_number: u64,
    /// Creation timestamp in nanoseconds since Unix epoch
    pub created_at_ns: u64,
}

impl EventSequence {
    /// Create a new event sequence
    pub fn new(sequence_number: u64) -> Self {
        Self {
            sequence_number,
            created_at_ns: std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap_or_default()
                .as_nanos() as u64,
        }
    }

    /// Get the sequence number
    pub const fn number(&self) -> u64 {
        self.sequence_number
    }

    /// Get the creation timestamp
    pub const fn timestamp(&self) -> u64 {
        self.created_at_ns
    }
}

/// Event metadata for additional context
#[derive(Debug, Clone, Serialize, Deserialize)]
/// Event metadata for additional context
///
/// Additional contextual information attached to events including
/// source identification, tags, custom data, and correlation tracking.
pub struct EventMetadata {
    /// Source of the event (e.g., "`trading_engine`", "`risk_manager`")
    pub source: String,
    /// Additional tags for filtering and search
    pub tags: HashMap<String, String>,
    /// Custom data specific to the event
    pub custom_data: Option<JsonValue>,
    /// Event correlation ID for tracking related events
    pub correlation_id: Option<String>,
    /// Session ID for grouping events by trading session
    pub session_id: Option<String>,
    /// User or system that triggered the event
    pub triggered_by: Option<String>,
}

impl EventMetadata {
    /// Create new metadata with source
    pub fn new(source: String) -> Self {
        Self {
            source,
            tags: HashMap::new(),
            custom_data: None,
            correlation_id: None,
            session_id: None,
            triggered_by: None,
        }
    }

    /// Add a tag
    pub fn with_tag(mut self, key: String, value: String) -> Self {
        self.tags.insert(key, value);
        self
    }

    /// Add correlation ID
    pub fn with_correlation_id(mut self, correlation_id: String) -> Self {
        self.correlation_id = Some(correlation_id);
        self
    }

    /// Add session ID
    pub fn with_session_id(mut self, session_id: String) -> Self {
        self.session_id = Some(session_id);
        self
    }

    /// Add custom data
    pub fn with_custom_data(mut self, data: JsonValue) -> Self {
        self.custom_data = Some(data);
        self
    }
}

/// Builder for creating trading events with proper metadata
#[derive(Debug)]
pub struct TradingEventBuilder {
    sequence_number: Option<u64>,
    metadata: Option<EventMetadata>,
}

impl TradingEventBuilder {
    /// Start building a new trading event
    pub const fn new() -> Self {
        Self {
            sequence_number: None,
            metadata: None,
        }
    }

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

    /// Set metadata
    pub fn with_metadata(mut self, metadata: EventMetadata) -> Self {
        self.metadata = Some(metadata);
        self
    }

    /// Build an order submitted event
    pub fn order_submitted(
        self,
        order_id: String,
        symbol: String,
        quantity: Decimal,
        price: Decimal,
    ) -> TradingEvent {
        TradingEvent::OrderSubmitted {
            order_id,
            symbol,
            quantity,
            price,
            timestamp: HardwareTimestamp::now(),
            sequence_number: self.sequence_number,
            metadata: self.metadata.and_then(|m| serde_json::to_value(m).ok()),
        }
    }

    /// Build an order executed event
    pub fn order_executed(
        self,
        trade_id: String,
        symbol: String,
        quantity: Decimal,
        price: Decimal,
    ) -> TradingEvent {
        TradingEvent::OrderExecuted {
            trade_id,
            symbol,
            quantity,
            price,
            timestamp: HardwareTimestamp::now(),
            sequence_number: self.sequence_number,
            metadata: self.metadata.and_then(|m| serde_json::to_value(m).ok()),
        }
    }

    /// Build a system event
    pub fn system_event(
        self,
        event_type: SystemEventType,
        message: String,
        level: EventLevel,
    ) -> TradingEvent {
        TradingEvent::SystemEvent {
            event_type,
            message,
            level,
            timestamp: HardwareTimestamp::now(),
            sequence_number: self.sequence_number,
            metadata: self.metadata.and_then(|m| serde_json::to_value(m).ok()),
        }
    }
}

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

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

    #[test]
    fn test_trading_event_creation() {
        let event = TradingEvent::OrderSubmitted {
            order_id: "TEST-001".to_string(),
            symbol: "EURUSD".to_string(),
            quantity: Decimal::new(100000, 0),
            price: Decimal::new(10850, 4),
            timestamp: HardwareTimestamp::now(),
            sequence_number: Some(1),
            metadata: None,
        };

        assert_eq!(event.event_type(), "order_submitted");
        assert_eq!(event.sequence_number(), Some(1));
        assert_eq!(event.symbol(), Some("EURUSD"));
        assert!(!event.is_critical());
    }

    #[test]
    fn test_event_level_ordering() {
        assert!(EventLevel::Critical > EventLevel::Error);
        assert!(EventLevel::Error > EventLevel::Warning);
        assert!(EventLevel::Warning > EventLevel::Info);
        assert!(EventLevel::Info > EventLevel::Debug);
    }

    #[test]
    fn test_alert_severity_ordering() {
        assert!(AlertSeverity::Critical > AlertSeverity::High);
        assert!(AlertSeverity::High > AlertSeverity::Medium);
        assert!(AlertSeverity::Medium > AlertSeverity::Low);
    }

    #[test]
    fn test_event_metadata() {
        let metadata = EventMetadata::new("test_source".to_string())
            .with_tag("environment".to_string(), "test".to_string())
            .with_correlation_id("corr-123".to_string());

        assert_eq!(metadata.source, "test_source");
        assert_eq!(metadata.tags.get("environment"), Some(&"test".to_string()));
        assert_eq!(metadata.correlation_id, Some("corr-123".to_string()));
    }

    #[test]
    fn test_trading_event_builder() {
        let metadata = EventMetadata::new("trading_engine".to_string())
            .with_tag("strategy".to_string(), "mean_reversion".to_string());

        let event = TradingEventBuilder::new()
            .with_sequence(123)
            .with_metadata(metadata)
            .order_submitted(
                "ORD-456".to_string(),
                "GBPUSD".to_string(),
                Decimal::new(50000, 0),
                Decimal::new(12750, 4),
            );

        assert_eq!(event.sequence_number(), Some(123));
        assert_eq!(event.symbol(), Some("GBPUSD"));
        assert!(event.metadata().is_some());
    }

    #[test]
    fn test_event_sequence() {
        let seq1 = EventSequence::new(1);
        let seq2 = EventSequence::new(2);

        assert!(seq2 > seq1);
        assert_eq!(seq1.number(), 1);
        assert!(seq1.timestamp() > 0);
    }

    #[test]
    fn test_event_description() {
        let event = TradingEvent::OrderSubmitted {
            order_id: "TEST-001".to_string(),
            symbol: "EURUSD".to_string(),
            quantity: Decimal::new(100000, 0),
            price: Decimal::new(10850, 4),
            timestamp: HardwareTimestamp::now(),
            sequence_number: Some(1),
            metadata: None,
        };

        let description = event.description();
        assert!(description.contains("Order TEST-001 submitted"));
        assert!(description.contains("100000 EURUSD"));
        assert!(description.contains("1.0850"));
    }

    #[test]
    fn test_risk_alert_event() {
        let event = TradingEvent::RiskAlert {
            alert_type: RiskAlertType::PositionSizeLimit,
            symbol: Some("EURUSD".to_string()),
            message: "Position size exceeded 80% of limit".to_string(),
            severity: AlertSeverity::High,
            timestamp: HardwareTimestamp::now(),
            sequence_number: Some(1),
            metadata: None,
        };

        assert!(event.is_critical());
        assert_eq!(event.level(), EventLevel::Error);
    }

    #[test]
    fn test_event_serialization() {
        let event = TradingEvent::OrderSubmitted {
            order_id: "TEST-001".to_string(),
            symbol: "EURUSD".to_string(),
            quantity: Decimal::new(100000, 0),
            price: Decimal::new(10850, 4),
            timestamp: HardwareTimestamp::now(),
            sequence_number: Some(1),
            metadata: None,
        };

        let serialized = serde_json::to_string(&event).expect("Event should serialize to JSON");
        let deserialized: TradingEvent =
            serde_json::from_str(&serialized).expect("JSON should deserialize back to event");

        assert_eq!(event.event_type(), deserialized.event_type());
        assert_eq!(event.sequence_number(), deserialized.sequence_number());
    }
}
