//! # Data Types Module
//!
//! Core data types and structures for market data ingestion, broker integration,
//! and financial data processing in the Foxhunt HFT trading system.
//!
//! This module provides:
//! - **Market Data Types**: Quote, Trade, Aggregate structures for market data
//! - **Extended Events**: Provider-specific events from Benzinga, Databento
//! - **Account & Position Types**: Trading account and position management
//! - **Time Range Utilities**: Time-based data queries and filtering
//!
//! ## Architecture
//!
//! The type system is designed around the canonical event types from the `common`
//! crate, with extensions for provider-specific data sources:
//!
//! ```text
//! ┌─────────────────────┐    ┌─────────────────────┐
//! │   Common Types      │────│   Extended Types    │
//! │   (MarketDataEvent) │    │   (Provider Events) │
//! └─────────────────────┘    └─────────────────────┘
//!           │                           │
//!           ▼                           ▼
//! ┌─────────────────────┐    ┌─────────────────────┐
//! │   Core Events       │    │   News/Sentiment    │
//! │   (Quote/Trade)     │    │   Analyst Ratings   │
//! └─────────────────────┘    └─────────────────────┘
//! ```
//!
//! ## Usage
//!
//! ```rust
//! use data::types::{ExtendedMarketDataEvent, TimeRange, Quote, Position};
//! use common::MarketDataEvent;
//! use rust_decimal::Decimal;
//!
//! // Create a time range for historical queries
//! let time_range = TimeRange {
//!     start: Utc::now() - Duration::days(1),
//!     end: Utc::now(),
//! };
//!
//! // Work with extended market data events
//! let extended_event = ExtendedMarketDataEvent::Core(
//!     MarketDataEvent::Quote(/* quote data */)
//! );
//!
//! // Extract symbol and timestamp
//! let symbol = extended_event.symbol();
//! let timestamp = extended_event.timestamp();
//! ```

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

/// Time range specification for historical data queries and filtering.
///
/// Used to specify date/time boundaries for retrieving historical market data,
/// backtesting periods, and data validation ranges.
///
/// # Examples
///
/// ```rust
/// use data::types::TimeRange;
/// use chrono::{Utc, Duration};
///
/// // Create a time range for the last 24 hours
/// let range = TimeRange {
///     start: Utc::now() - Duration::days(1),
///     end: Utc::now(),
/// };
///
/// // Validate the range is meaningful
/// assert!(range.end > range.start);
/// ```
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct TimeRange {
    /// Start time (inclusive) in UTC timezone
    pub start: DateTime<Utc>,
    /// End time (exclusive) in UTC timezone
    pub end: DateTime<Utc>,
}

impl TimeRange {
    /// Create a new time range with validation.
    ///
    /// Ensures that the end time is after the start time to prevent
    /// invalid ranges that could cause issues in data queries.
    ///
    /// # Parameters
    ///
    /// * `start` - Start time (inclusive)
    /// * `end` - End time (exclusive)
    ///
    /// # Returns
    ///
    /// `Ok(TimeRange)` if valid, `Err(String)` if end <= start.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::TimeRange;
    /// use chrono::{Utc, Duration};
    ///
    /// let now = Utc::now();
    /// let range = TimeRange::new(
    ///     now - Duration::hours(1),
    ///     now
    /// ).unwrap();
    /// ```
    pub fn new(start: DateTime<Utc>, end: DateTime<Utc>) -> Result<Self, String> {
        if end <= start {
            return Err(format!(
                "End time ({}) must be after start time ({})",
                end, start
            ));
        }
        Ok(Self { start, end })
    }

    /// Create a time range for the last N hours from now.
    ///
    /// Convenience method for creating ranges relative to the current time.
    ///
    /// # Parameters
    ///
    /// * `hours` - Number of hours back from now
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::TimeRange;
    ///
    /// // Last 24 hours
    /// let range = TimeRange::last_hours(24);
    /// ```
    pub fn last_hours(hours: i64) -> Self {
        let now = Utc::now();
        Self {
            start: now - Duration::hours(hours),
            end: now,
        }
    }

    /// Create a time range for the last N days from now.
    ///
    /// Convenience method for creating ranges spanning multiple days.
    ///
    /// # Parameters
    ///
    /// * `days` - Number of days back from now
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::TimeRange;
    ///
    /// // Last 7 days
    /// let range = TimeRange::last_days(7);
    /// ```
    pub fn last_days(days: i64) -> Self {
        let now = Utc::now();
        Self {
            start: now - Duration::days(days),
            end: now,
        }
    }

    /// Create a time range for the last N minutes from now.
    ///
    /// Convenience method for creating short-term ranges.
    ///
    /// # Parameters
    ///
    /// * `minutes` - Number of minutes back from now
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::TimeRange;
    ///
    /// // Last 30 minutes
    /// let range = TimeRange::last_minutes(30);
    /// ```
    pub fn last_minutes(minutes: i64) -> Self {
        let now = Utc::now();
        Self {
            start: now - Duration::minutes(minutes),
            end: now,
        }
    }

    /// Check if this time range overlaps with another range.
    ///
    /// # Parameters
    ///
    /// * `other` - Another time range to check for overlap
    ///
    /// # Returns
    ///
    /// `true` if the ranges overlap, `false` otherwise
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::TimeRange;
    /// use chrono::{Utc, Duration};
    ///
    /// let now = Utc::now();
    /// let range1 = TimeRange::new(now - Duration::hours(2), now).unwrap();
    /// let range2 = TimeRange::new(now - Duration::hours(1), now + Duration::hours(1)).unwrap();
    /// assert!(range1.overlaps(&range2));
    /// ```
    pub fn overlaps(&self, other: &TimeRange) -> bool {
        self.start < other.end && other.start < self.end
    }

    /// Get the duration of this time range.
    ///
    /// # Returns
    ///
    /// The time span covered by this range.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::TimeRange;
    /// use chrono::Duration;
    ///
    /// let range = TimeRange::last_hours(24);
    /// assert_eq!(range.duration().num_hours(), 24);
    /// ```
    pub fn duration(&self) -> Duration {
        self.end - self.start
    }

    /// Check if a timestamp falls within this time range.
    ///
    /// # Parameters
    ///
    /// * `timestamp` - The timestamp to check
    ///
    /// # Returns
    ///
    /// `true` if timestamp is within [start, end), `false` otherwise.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::TimeRange;
    /// use chrono::Utc;
    ///
    /// let range = TimeRange::last_hours(1);
    /// let now = Utc::now();
    /// assert!(range.contains(now - Duration::minutes(30)));
    /// assert!(!range.contains(now - Duration::hours(2)));
    /// ```
    pub fn contains(&self, timestamp: DateTime<Utc>) -> bool {
        timestamp >= self.start && timestamp < self.end
    }

    /// Split this time range into smaller chunks of the specified duration.
    ///
    /// Useful for breaking large time ranges into manageable pieces for
    /// batch processing or API rate limiting.
    ///
    /// # Parameters
    ///
    /// * `chunk_duration` - Size of each chunk
    ///
    /// # Returns
    ///
    /// Vector of time ranges covering the original range.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::TimeRange;
    /// use chrono::Duration;
    ///
    /// let range = TimeRange::last_hours(24);
    /// let chunks = range.split_into_chunks(Duration::hours(1));
    /// assert_eq!(chunks.len(), 24);
    /// ```
    pub fn split_into_chunks(&self, chunk_duration: Duration) -> Vec<TimeRange> {
        let mut chunks = Vec::new();
        let mut current_start = self.start;

        while current_start < self.end {
            let current_end = std::cmp::min(current_start + chunk_duration, self.end);
            chunks.push(TimeRange {
                start: current_start,
                end: current_end,
            });
            current_start = current_end;
        }

        chunks
    }
}

/// Market data type classification for subscription and routing purposes.
///
/// Defines the types of market data that can be requested from data providers
/// and processed by the trading system. Each type corresponds to different
/// levels of market information granularity.
///
/// # Market Data Hierarchy
///
/// - **Quotes**: Best bid/offer (Level 1)
/// - **Trades**: Executed transactions
/// - **Aggregates**: Time-based OHLCV bars
/// - **Level2**: Full order book depth
/// - **Status**: Market session information
///
/// # Examples
///
/// ```rust
/// use data::types::MarketDataType;
///
/// // Request real-time quotes for algorithmic trading
/// let quote_type = MarketDataType::Quotes;
///
/// // Request trade data for execution analysis
/// let trade_type = MarketDataType::Trades;
///
/// // Request Level 2 data for market microstructure analysis
/// let level2_type = MarketDataType::Level2;
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MarketDataType {
    /// Real-time bid/ask quotes (Level 1 market data)
    ///
    /// Contains the best bid and offer prices with sizes, updated in real-time
    /// as the top of the order book changes. Essential for spread analysis
    /// and basic trading decisions.
    Quotes,

    /// Executed trade transactions with price, size, and conditions
    ///
    /// Individual trade executions that have occurred in the market,
    /// including trade price, volume, exchange, and any special conditions.
    /// Used for volume analysis and trade pattern recognition.
    Trades,

    /// Time-aggregated OHLCV bars (Open, High, Low, Close, Volume)
    ///
    /// Summarized price action over fixed time intervals (1min, 5min, 1hour, etc.).
    /// Essential for technical analysis, charting, and strategy backtesting.
    Aggregates,

    /// Level 2 order book data with full market depth
    ///
    /// Complete order book showing multiple price levels on both bid and ask sides.
    /// Critical for microstructure analysis, liquidity assessment, and sophisticated
    /// execution algorithms.
    Level2,

    /// Market session status and trading halt information
    ///
    /// Information about market open/close times, trading halts, circuit breakers,
    /// and other market-wide events that affect trading operations.
    Status,
}

// Import MarketDataEvent from common crate - use common::MarketDataEvent directly

/// Extended market data event enumeration with provider-specific events.
///
/// Wraps the core `MarketDataEvent` types with additional events from specialized
/// data providers like Benzinga (news, sentiment, analyst ratings) that provide
/// fundamental and alternative data beyond traditional market data.
///
/// This design allows the trading system to handle both:
/// - **Core Market Data**: Price, volume, order book updates
/// - **Alternative Data**: News sentiment, analyst ratings, unusual activity
///
/// # Event Processing Pipeline
///
/// ```text
/// Provider Events → ExtendedMarketDataEvent → Event Router → Strategy Engine
///                                          ↓
///                                   Feature Extraction
/// ```
///
/// # Examples
///
/// ```rust
/// use data::types::ExtendedMarketDataEvent;
/// use common::MarketDataEvent;
///
/// // Core market data event
/// let core_event = ExtendedMarketDataEvent::Core(
///     MarketDataEvent::Quote(/* quote data */)
/// );
///
/// // News event from Benzinga
/// let news_event = ExtendedMarketDataEvent::NewsAlert(
///     /* Benzinga news data */
/// );
///
/// // Extract common properties
/// let symbol = core_event.symbol();
/// let timestamp = core_event.timestamp();
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExtendedMarketDataEvent {
    /// Core market data event (quotes, trades, level2, etc.)
    ///
    /// Standard market data events from the canonical event system,
    /// including real-time quotes, trades, aggregates, and order book updates.
    Core(::common::MarketDataEvent),

    /// News alerts and breaking news events (Benzinga)
    ///
    /// Real-time news alerts that may impact stock prices, including
    /// earnings announcements, FDA approvals, analyst upgrades/downgrades,
    /// and other market-moving news events.
    NewsAlert(crate::providers::common::NewsEvent),

    /// Market sentiment updates and analysis (Benzinga)
    ///
    /// Sentiment analysis scores derived from news articles, social media,
    /// and other textual data sources. Used for incorporating market mood
    /// into trading decisions.
    SentimentUpdate(crate::providers::common::SentimentEvent),

    /// Analyst rating changes and research reports (Benzinga)
    ///
    /// Analyst upgrades, downgrades, price target changes, and initiation
    /// of coverage from sell-side research firms. Critical for fundamental
    /// analysis and momentum strategies.
    AnalystRating(crate::providers::common::AnalystRatingEvent),

    /// Unusual options activity and flow alerts (Benzinga)
    ///
    /// Detection of unusual options trading patterns that may indicate
    /// informed trading or upcoming corporate events. Used for identifying
    /// potential price catalysts.
    UnusualOptions(crate::providers::common::UnusualOptionsEvent),
}

// Import canonical event types from common crate - use common::QuoteEvent directly
// Unused imports removed - use common crate directly
/// Real-time bid/ask quote data structure.
///
/// Represents the best bid and offer (BBO) prices and sizes at a given moment.
/// This is Level 1 market data that provides the top of the order book for
/// a specific symbol.
///
/// # Usage in Trading
///
/// - **Spread Analysis**: Calculate bid-ask spread for liquidity assessment
/// - **Market Making**: Determine optimal quote placement
/// - **Execution Timing**: Assess market conditions before order placement
/// - **Risk Management**: Monitor market impact and slippage potential
///
/// # Examples
///
/// ```rust
/// use data::types::Quote;
/// use rust_decimal::Decimal;
/// use chrono::Utc;
///
/// let quote = Quote {
///     symbol: "AAPL".to_string(),
///     bid: Decimal::new(15000, 2), // $150.00
///     ask: Decimal::new(15001, 2), // $150.01
///     bid_size: Decimal::new(100, 0), // 100 shares
///     ask_size: Decimal::new(200, 0), // 200 shares
///     exchange: Some("NASDAQ".to_string()),
///     timestamp: Utc::now(),
/// };
///
/// // Calculate spread
/// let spread = quote.ask - quote.bid; // $0.01
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Quote {
    /// Security symbol (ticker)
    ///
    /// Standard symbol identifier (e.g., "AAPL", "GOOGL", "SPY")
    pub symbol: String,

    /// Highest price buyers are willing to pay
    ///
    /// Best bid price in the order book, representing the highest price
    /// at which someone is willing to buy the security.
    pub bid: Decimal,

    /// Lowest price sellers are willing to accept
    ///
    /// Best ask (offer) price in the order book, representing the lowest price
    /// at which someone is willing to sell the security.
    pub ask: Decimal,

    /// Number of shares available at the bid price
    ///
    /// Total size/volume available at the best bid price level.
    pub bid_size: Decimal,

    /// Number of shares available at the ask price
    ///
    /// Total size/volume available at the best ask price level.
    pub ask_size: Decimal,

    /// Exchange or market center providing the quote
    ///
    /// Optional identifier for the specific exchange or market maker
    /// providing this quote (e.g., "NASDAQ", "NYSE", "BATS").
    pub exchange: Option<String>,

    /// Quote timestamp in UTC
    ///
    /// When this quote was generated or last updated by the exchange.
    pub timestamp: DateTime<Utc>,
}

/// Executed trade transaction data structure.
///
/// Represents an individual trade execution that occurred in the market,
/// including price, volume, exchange, and any special trade conditions.
/// This data is essential for:
///
/// - **Volume Analysis**: Understanding trading activity patterns
/// - **Price Discovery**: Observing actual transaction prices
/// - **Execution Quality**: Analyzing trade execution performance
/// - **Market Microstructure**: Studying trade flow and market impact
///
/// # Trade Conditions
///
/// The `conditions` field contains exchange-specific condition codes that
/// indicate special circumstances of the trade (e.g., opening/closing auctions,
/// odd lots, block trades, etc.).
///
/// # Examples
///
/// ```rust
/// use data::types::Trade;
/// use rust_decimal::Decimal;
/// use chrono::Utc;
///
/// let trade = Trade {
///     symbol: "AAPL".to_string(),
///     price: Decimal::new(15000, 2), // $150.00
///     size: Decimal::new(100, 0),    // 100 shares
///     exchange: Some("NASDAQ".to_string()),
///     conditions: vec![],             // Normal trade
///     timestamp: Utc::now(),
/// };
///
/// // Calculate trade value
/// let value = trade.price * trade.size; // $15,000
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "database", derive(sqlx::FromRow))]
pub struct Trade {
    /// Security symbol (ticker)
    ///
    /// Standard symbol identifier for the traded security.
    pub symbol: String,

    /// Execution price of the trade
    ///
    /// The price at which the trade was executed, representing the
    /// agreed-upon value between buyer and seller.
    pub price: Decimal,

    /// Trade volume (number of shares/contracts)
    ///
    /// The quantity of securities that changed hands in this transaction.
    pub size: Decimal,

    /// Exchange or venue where the trade occurred
    ///
    /// Optional identifier for the specific exchange, ECN, or dark pool
    /// where this trade was executed.
    pub exchange: Option<String>,

    /// Exchange-specific trade condition codes
    ///
    /// Array of condition codes that provide additional context about
    /// the trade (e.g., opening print, closing print, odd lot, block trade).
    /// Condition code meanings are exchange-specific.
    pub conditions: Vec<u32>,

    /// Trade execution timestamp in UTC
    ///
    /// The exact time when this trade was executed.
    pub timestamp: DateTime<Utc>,
}

// Aggregate moved to common::Aggregate

// Level2Update, PriceLevel, and MarketStatus moved to common::types

// Subscription and DataType moved to common::types

// ConnectionEvent, ConnectionStatus, and ErrorEvent moved to common::types

// OrderEvent is imported from common::prelude as part of the canonical event system
// See: common::events::OrderEvent

// OrderStatus is imported from common::prelude as part of the canonical type system
// See: common::basic::OrderStatus

/// Trading position information and P&L tracking.
///
/// Represents a current position in a security, including size, entry price,
/// and profit/loss calculations. Essential for:
///
/// - **Risk Management**: Position sizing and exposure monitoring
/// - **P&L Tracking**: Real-time profit/loss calculation
/// - **Portfolio Management**: Asset allocation and rebalancing
/// - **Performance Analysis**: Trade outcome evaluation
///
/// # Position Sizing Convention
///
/// - **Positive size**: Long position (owns the security)
/// - **Negative size**: Short position (borrowed and sold the security)
/// - **Zero size**: No position (flat)
///
/// # P&L Calculation
///
/// - **Unrealized P&L**: (Current Price - Avg Entry Price) × Position Size
/// - **Realized P&L**: Cumulative profit/loss from closed portions of the position
///
/// # Examples
///
/// ```rust
/// use data::types::Position;
/// use rust_decimal::Decimal;
/// use chrono::Utc;
///
/// let position = Position {
///     symbol: "AAPL".to_string(),
///     size: Decimal::new(100, 0),        // Long 100 shares
///     avg_price: Decimal::new(15000, 2), // Avg entry at $150.00
///     unrealized_pnl: Decimal::new(50000, 2), // $500 unrealized gain
///     realized_pnl: Decimal::new(10000, 2),   // $100 realized gain
///     market_value: Decimal::new(1550000, 2), // $15,500 current value
///     timestamp: Utc::now(),
/// };
///
/// // Calculate current price
/// let current_price = position.market_value / position.size; // $155.00
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Position {
    /// Security symbol (ticker)
    ///
    /// Identifier for the security held in this position.
    pub symbol: String,

    /// Position size with directional sign
    ///
    /// Number of shares/contracts held:
    /// - Positive: Long position
    /// - Negative: Short position
    /// - Zero: Flat (no position)
    pub size: Decimal,

    /// Volume-weighted average entry price
    ///
    /// The average price paid for all shares currently held in the position,
    /// calculated as a volume-weighted average of all entry transactions.
    pub avg_price: Decimal,

    /// Unrealized profit/loss (mark-to-market)
    ///
    /// Current profit or loss based on the difference between the average
    /// entry price and the current market price, multiplied by position size.
    pub unrealized_pnl: Decimal,

    /// Realized profit/loss from closed portions
    ///
    /// Cumulative profit or loss from portions of the position that have
    /// been closed (sold if long, covered if short).
    pub realized_pnl: Decimal,

    /// Current market value of the position
    ///
    /// Position size multiplied by the current market price.
    /// For short positions, this represents the liability.
    pub market_value: Decimal,

    /// Last position update timestamp
    ///
    /// When this position information was last updated with fresh market data.
    pub timestamp: DateTime<Utc>,
}

/// Trading account information and margin calculations.
///
/// Comprehensive account data including equity, cash balances, buying power,
/// and margin requirements. Critical for:
///
/// - **Risk Management**: Ensuring sufficient capital for positions
/// - **Position Sizing**: Calculating maximum allowable position sizes
/// - **Margin Compliance**: Avoiding margin calls and forced liquidations
/// - **Capital Allocation**: Optimizing capital usage across strategies
///
/// # Margin Types
///
/// - **Initial Margin**: Required capital to open a new position
/// - **Maintenance Margin**: Minimum equity required to keep positions open
/// - **Day Trading Buying Power**: Enhanced buying power for day trading accounts
///
/// # Account Monitoring
///
/// Real-time account monitoring is essential for automated trading systems
/// to prevent over-leveraging and ensure regulatory compliance.
///
/// # Examples
///
/// ```rust
/// use data::types::Account;
/// use rust_decimal::Decimal;
/// use chrono::Utc;
///
/// let account = Account {
///     account_id: "DU123456".to_string(),
///     total_equity: Decimal::new(10000000, 2),     // $100,000
///     available_cash: Decimal::new(5000000, 2),    // $50,000
///     buying_power: Decimal::new(20000000, 2),     // $200,000 (2:1 margin)
///     day_trading_buying_power: Decimal::new(40000000, 2), // $400,000 (4:1 intraday)
///     maintenance_margin: Decimal::new(2500000, 2), // $25,000
///     initial_margin: Decimal::new(5000000, 2),    // $50,000
///     timestamp: Utc::now(),
/// };
///
/// // Check if account can support a $30,000 position
/// let position_value = Decimal::new(3000000, 2); // $30,000
/// let can_trade = account.buying_power >= position_value;
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Account {
    /// Unique account identifier
    ///
    /// Broker-specific account number or identifier used for
    /// routing orders and tracking positions.
    pub account_id: String,

    /// Total account equity (cash + positions)
    ///
    /// Net liquidation value of the account, including cash balance
    /// and the current market value of all positions.
    pub total_equity: Decimal,

    /// Available cash balance
    ///
    /// Cash available for immediate use, not tied up in positions
    /// or pending settlements.
    pub available_cash: Decimal,

    /// Standard buying power (typically 2:1 for stocks)
    ///
    /// Maximum dollar amount that can be used to purchase securities,
    /// considering margin requirements and current positions.
    pub buying_power: Decimal,

    /// Enhanced buying power for day trading (typically 4:1)
    ///
    /// Increased buying power available for intraday positions that
    /// will be closed before market close. Subject to PDT rules.
    pub day_trading_buying_power: Decimal,

    /// Maintenance margin requirement
    ///
    /// Minimum equity that must be maintained in the account to
    /// keep current positions open. Falling below triggers margin call.
    pub maintenance_margin: Decimal,

    /// Initial margin requirement
    ///
    /// Required equity to open new margined positions.
    /// Typically higher than maintenance margin.
    pub initial_margin: Decimal,

    /// Last account data update timestamp
    ///
    /// When this account information was last refreshed from the broker.
    pub timestamp: DateTime<Utc>,
}

impl ExtendedMarketDataEvent {
    /// Extract the symbol identifier from any extended market data event.
    ///
    /// Returns the symbol associated with this event, regardless of whether it's
    /// a core market data event or a provider-specific event. For news events
    /// without a specific symbol, returns an empty string.
    ///
    /// # Returns
    ///
    /// A string slice containing the symbol, or empty string if not applicable.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::ExtendedMarketDataEvent;
    /// use common::MarketDataEvent;
    ///
    /// let event = ExtendedMarketDataEvent::Core(
    ///     MarketDataEvent::Quote(/* AAPL quote */)
    /// );
    /// assert_eq!(event.symbol(), "AAPL");
    /// ```
    pub fn symbol(&self) -> &str {
        match self {
            ExtendedMarketDataEvent::Core(event) => event.symbol(),
            ExtendedMarketDataEvent::NewsAlert(n) => {
                // For news events, return symbol if available, otherwise empty string
                n.symbol.as_ref().map(|s| s.as_str()).unwrap_or("")
            },
            ExtendedMarketDataEvent::SentimentUpdate(s) => s.symbol.as_str(),
            ExtendedMarketDataEvent::AnalystRating(a) => a.symbol.as_str(),
            ExtendedMarketDataEvent::UnusualOptions(u) => u.symbol.as_str(),
        }
    }

    /// Extract the timestamp from any extended market data event.
    ///
    /// Returns the timestamp when this event occurred or was generated.
    /// All event types should have timestamps for proper sequencing and
    /// time-based analysis.
    ///
    /// # Returns
    ///
    /// `Some(timestamp)` for events with timestamps, `None` if unavailable.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::ExtendedMarketDataEvent;
    /// use chrono::Utc;
    ///
    /// let event = ExtendedMarketDataEvent::NewsAlert(/* news event */);
    /// if let Some(timestamp) = event.timestamp() {
    ///     println!("Event occurred at: {}", timestamp);
    /// }
    /// ```
    pub fn timestamp(&self) -> Option<DateTime<Utc>> {
        match self {
            ExtendedMarketDataEvent::Core(event) => event.timestamp(),
            ExtendedMarketDataEvent::NewsAlert(n) => Some(n.timestamp),
            ExtendedMarketDataEvent::SentimentUpdate(s) => Some(s.timestamp),
            ExtendedMarketDataEvent::AnalystRating(a) => Some(a.timestamp),
            ExtendedMarketDataEvent::UnusualOptions(u) => Some(u.timestamp),
        }
    }

    /// Convert an extended market data event to a core market data event.
    ///
    /// This method extracts the core `MarketDataEvent` from the extended wrapper,
    /// if one exists. Provider-specific events (news, sentiment, etc.) cannot be
    /// converted to core events since they represent different data types.
    ///
    /// # Returns
    ///
    /// - `Some(MarketDataEvent)` for Core events
    /// - `None` for provider-specific events that have no core equivalent
    ///
    /// # Use Cases
    ///
    /// - Filtering out alternative data to focus on price/volume data
    /// - Converting to systems that only handle core market data
    /// - Separating traditional and alternative data streams
    ///
    /// # Examples
    ///
    /// ```rust
    /// use data::types::ExtendedMarketDataEvent;
    ///
    /// let extended_event = ExtendedMarketDataEvent::Core(/* market data */);
    /// if let Some(core_event) = extended_event.into_core_event() {
    ///     // Process as standard market data
    /// }
    ///
    /// let news_event = ExtendedMarketDataEvent::NewsAlert(/* news */);
    /// assert!(news_event.into_core_event().is_none());
    /// ```
    pub fn into_core_event(self) -> Option<common::MarketDataEvent> {
        match self {
            ExtendedMarketDataEvent::Core(event) => Some(event),
            _ => None, // Provider-specific events don't have core equivalents
        }
    }
}

/// Extract core market data events from a collection of extended events.
///
/// Filters a vector of `ExtendedMarketDataEvent` to extract only the core
/// `MarketDataEvent` instances, discarding provider-specific events like
/// news alerts and sentiment updates.
///
/// This is useful when you need to process only traditional market data
/// (quotes, trades, level2) and ignore alternative data sources.
///
/// # Parameters
///
/// * `extended_events` - Collection of extended market data events to filter
///
/// # Returns
///
/// Vector containing only the core market data events, preserving order.
///
/// # Examples
///
/// ```rust
/// use data::types::{ExtendedMarketDataEvent, extract_core_events};
/// use common::MarketDataEvent;
///
/// let extended_events = vec![
///     ExtendedMarketDataEvent::Core(/* quote event */),
///     ExtendedMarketDataEvent::NewsAlert(/* news event */),
///     ExtendedMarketDataEvent::Core(/* trade event */),
/// ];
///
/// let core_events = extract_core_events(extended_events);
/// // Result contains only the quote and trade events
/// assert_eq!(core_events.len(), 2);
/// ```
///
/// # Performance
///
/// This function uses iterator combinators for efficient filtering without
/// intermediate allocations beyond the final result vector.
pub fn extract_core_events(
    extended_events: Vec<ExtendedMarketDataEvent>,
) -> Vec<common::MarketDataEvent> {
    extended_events
        .into_iter()
        .filter_map(|event| event.into_core_event())
        .collect()
}

/// Extract timestamp from any core market data event.
///
/// Utility function to extract the timestamp from a `MarketDataEvent` since
/// we cannot add methods to the enum defined in the common crate. Different
/// event types store timestamps in different fields, so this function handles
/// the pattern matching.
///
/// # Parameters
///
/// * `event` - Reference to a core market data event
///
/// # Returns
///
/// `Some(timestamp)` for all event types, or `None` if timestamp is missing
/// (though all current event types include timestamps).
///
/// # Event Timestamp Fields
///
/// - **Quote/Trade/Level2**: Direct `timestamp` field
/// - **Aggregate/Bar**: Uses `end_timestamp` (when the period ended)
/// - **Status/Connection/Error**: Direct `timestamp` field
/// - **OrderBook events**: Direct `timestamp` field
///
/// # Examples
///
/// ```rust
/// use data::types::get_event_timestamp;
/// use common::MarketDataEvent;
///
/// let event = MarketDataEvent::Quote(/* quote data */);
/// if let Some(timestamp) = get_event_timestamp(&event) {
///     println!("Event timestamp: {}", timestamp);
/// }
/// ```
///
/// # Use Cases
///
/// - Event sequencing and ordering
/// - Latency analysis and performance monitoring
/// - Time-based filtering and windowing
/// - Synchronization across data sources
pub fn get_event_timestamp(event: &common::MarketDataEvent) -> Option<DateTime<Utc>> {
    match event {
        common::MarketDataEvent::Quote(q) => Some(q.timestamp),
        common::MarketDataEvent::Trade(t) => Some(t.timestamp),
        common::MarketDataEvent::Aggregate(a) => Some(a.end_timestamp),
        common::MarketDataEvent::Bar(b) => Some(b.end_timestamp),
        common::MarketDataEvent::Level2(l) => Some(l.timestamp),
        common::MarketDataEvent::Status(s) => Some(s.timestamp),
        common::MarketDataEvent::ConnectionStatus(c) => Some(c.timestamp),
        common::MarketDataEvent::Error(e) => Some(e.timestamp),
        common::MarketDataEvent::OrderBook(o) => Some(o.timestamp),
        common::MarketDataEvent::OrderBookL2Snapshot(s) => Some(s.timestamp),
        common::MarketDataEvent::OrderBookL2Update(u) => Some(u.timestamp),
    }
}

// Note: Subscription implementation moved to common crate
// Use common::Subscription methods

#[cfg(test)]
#[allow(clippy::assertions_on_result_states)]
mod tests {
    use super::*;
    use chrono::{Duration, Utc};

    #[test]
    fn test_time_range_creation() {
        let now = Utc::now();
        let start = now - Duration::hours(1);
        let end = now;

        let range = TimeRange::new(start, end).unwrap();
        assert_eq!(range.start, start);
        assert_eq!(range.end, end);
    }

    #[test]
    fn test_time_range_validation() {
        let now = Utc::now();
        let result = TimeRange::new(now, now - Duration::hours(1));
        assert!(result.is_err());
    }

    #[test]
    fn test_time_range_contains() {
        let range = TimeRange::last_hours(2);
        let now = Utc::now();

        assert!(range.contains(now - Duration::minutes(30)));
        assert!(!range.contains(now - Duration::hours(3)));
        assert!(!range.contains(now + Duration::minutes(30)));
    }

    #[test]
    fn test_time_range_split() {
        let range = TimeRange::last_hours(4);
        let chunks = range.split_into_chunks(Duration::hours(1));

        assert_eq!(chunks.len(), 4);
        for chunk in &chunks {
            assert_eq!(chunk.duration(), Duration::hours(1));
        }
    }

    #[test]
    fn test_subscription_creation() {
        let sub = common::Subscription {
            symbols: vec!["AAPL".to_string(), "GOOGL".to_string()],
            data_types: vec![common::DataType::Quotes],
            exchanges: vec![],
        };
        assert_eq!(sub.symbols.len(), 2);
        assert_eq!(sub.data_types.len(), 1);
        assert!(matches!(sub.data_types[0], common::DataType::Quotes));
    }

    #[test]
    fn test_market_data_event_symbol() {
        let quote = common::MarketDataEvent::Quote(common::QuoteEvent {
            symbol: "AAPL".to_string(),
            bid: Some(Decimal::new(15000, 2)), // 150.00
            ask: Some(Decimal::new(15001, 2)), // 150.01
            bid_size: Some(Decimal::new(100, 0)),
            ask_size: Some(Decimal::new(200, 0)),
            exchange: Some("NASDAQ".to_string()),
            bid_exchange: Some("NASDAQ".to_string()),
            ask_exchange: Some("NASDAQ".to_string()),
            conditions: vec![],
            timestamp: Utc::now(),
            sequence: 0,
        });

        assert_eq!(quote.symbol(), "AAPL");
    }

    #[test]
    fn test_extended_event_symbol_extraction() {
        // Test would need actual event instances to work properly
        // Placeholder test for extended event functionality
    }

    #[test]
    fn test_extract_core_events() {
        // Test would need actual event instances to work properly
        // Placeholder test for core event extraction
    }

    #[test]
    fn test_time_range_duration() {
        let start = Utc::now();
        let end = start + Duration::hours(2);
        let range = TimeRange::new(start, end).unwrap();

        assert_eq!(range.duration(), Duration::hours(2));
    }

    #[test]
    fn test_time_range_last_days() {
        let range = TimeRange::last_days(7);
        let duration = range.duration();

        // Should be approximately 7 days (allow small tolerance)
        assert!((duration - Duration::days(7)).num_seconds().abs() < 10);
    }

    #[test]
    fn test_time_range_last_minutes() {
        let range = TimeRange::last_minutes(30);
        let duration = range.duration();

        // Should be approximately 30 minutes
        assert!((duration - Duration::minutes(30)).num_seconds().abs() < 10);
    }

    #[test]
    fn test_time_range_overlaps() {
        let range1 = TimeRange {
            start: Utc::now() - Duration::hours(2),
            end: Utc::now(),
        };
        let range2 = TimeRange {
            start: Utc::now() - Duration::hours(1),
            end: Utc::now() + Duration::hours(1),
        };

        assert!(range1.overlaps(&range2));
        assert!(range2.overlaps(&range1));
    }

    #[test]
    fn test_time_range_no_overlap() {
        let range1 = TimeRange {
            start: Utc::now() - Duration::hours(3),
            end: Utc::now() - Duration::hours(2),
        };
        let range2 = TimeRange {
            start: Utc::now() - Duration::hours(1),
            end: Utc::now(),
        };

        assert!(!range1.overlaps(&range2));
        assert!(!range2.overlaps(&range1));
    }

    #[test]
    fn test_time_range_split_exact() {
        let range = TimeRange::last_hours(6);
        let chunks = range.split_into_chunks(Duration::hours(2));

        assert_eq!(chunks.len(), 3);

        // Verify each chunk is 2 hours
        for chunk in &chunks {
            assert_eq!(chunk.duration(), Duration::hours(2));
        }

        // Verify chunks are contiguous
        for i in 0..chunks.len() - 1 {
            assert_eq!(chunks[i].end, chunks[i + 1].start);
        }
    }

    #[test]
    fn test_time_range_split_uneven() {
        let range = TimeRange::last_hours(5);
        let chunks = range.split_into_chunks(Duration::hours(2));

        // Should have 3 chunks (2h + 2h + 1h)
        assert_eq!(chunks.len(), 3);

        // First two should be 2 hours
        assert_eq!(chunks[0].duration(), Duration::hours(2));
        assert_eq!(chunks[1].duration(), Duration::hours(2));

        // Last chunk should be approximately 1 hour (with small tolerance)
        let last_duration = chunks[2].duration();
        assert!((last_duration - Duration::hours(1)).num_seconds().abs() < 10);
    }

    #[test]
    fn test_get_event_timestamp_quote() {
        let timestamp = Utc::now();
        let quote = common::MarketDataEvent::Quote(common::QuoteEvent {
            symbol: "BTC".to_string(),
            bid: Some(Decimal::new(50000, 0)),
            ask: Some(Decimal::new(50001, 0)),
            bid_size: Some(Decimal::ONE),
            ask_size: Some(Decimal::ONE),
            exchange: Some("COINBASE".to_string()),
            bid_exchange: Some("COINBASE".to_string()),
            ask_exchange: Some("COINBASE".to_string()),
            conditions: vec![],
            timestamp,
            sequence: 1,
        });

        assert_eq!(get_event_timestamp(&quote), Some(timestamp));
    }

    #[test]
    fn test_get_event_timestamp_trade() {
        let timestamp = Utc::now();
        let trade = common::MarketDataEvent::Trade(common::TradeEvent {
            symbol: "ETH".to_string(),
            price: Decimal::new(3000, 0),
            size: Decimal::new(10, 0),
            exchange: Some("BINANCE".to_string()),
            conditions: vec![],
            timestamp,
            trade_id: Some("trade123".to_string()),
            sequence: 1,
        });

        assert_eq!(get_event_timestamp(&trade), Some(timestamp));
    }

    #[test]
    fn test_get_event_timestamp_aggregate() {
        let end_timestamp = Utc::now();
        let agg = common::MarketDataEvent::Aggregate(common::Aggregate {
            symbol: "AAPL".to_string(),
            open: Decimal::new(150, 0),
            high: Decimal::new(152, 0),
            low: Decimal::new(149, 0),
            close: Decimal::new(151, 0),
            volume: Decimal::new(10000, 0),
            vwap: Some(Decimal::new(15050, 2)),
            start_timestamp: end_timestamp - Duration::minutes(1),
            end_timestamp,
        });

        assert_eq!(get_event_timestamp(&agg), Some(end_timestamp));
    }

    #[test]
    fn test_subscription_multiple_symbols() {
        let sub = common::Subscription {
            symbols: vec!["BTC".to_string(), "ETH".to_string(), "SOL".to_string()],
            data_types: vec![common::DataType::Quotes, common::DataType::Trades],
            exchanges: vec!["COINBASE".to_string()],
        };

        assert_eq!(sub.symbols.len(), 3);
        assert_eq!(sub.data_types.len(), 2);
        assert_eq!(sub.exchanges.len(), 1);
    }

    #[test]
    fn test_time_range_edge_cases() {
        let now = Utc::now();

        // Same timestamp should fail
        let result = TimeRange::new(now, now);
        assert!(result.is_err());

        // Very small range should succeed
        let result = TimeRange::new(now, now + Duration::milliseconds(1));
        assert!(result.is_ok());
    }
}
