//! Comprehensive tests for market_data module
//!
//! Tests cover:
//! - MarketDataEvent creation and variants
//! - Trade, Quote, Bar, OrderBook, and News events
//! - Timestamp extraction from events
//! - Serialization/deserialization
//! - BarInterval variants

use chrono::Utc;
use common::market_data::{
    BarEvent, BarInterval, MarketDataEvent, NewsEvent, OrderBookEvent, QuoteEvent, TradeEvent,
};
use common::types::{OrderSide, Price, Quantity, Symbol};

// ============================================================================
// TradeEvent Tests
// ============================================================================

#[test]
fn test_trade_event_creation() {
    let symbol = Symbol::new("AAPL".to_string());
    let price = Price::from_f64(150.50).unwrap();
    let quantity = Quantity::from_f64(100.0).unwrap();
    let timestamp = Utc::now();

    let trade = TradeEvent {
        symbol: symbol.clone(),
        price,
        quantity,
        side: OrderSide::Buy,
        timestamp,
        trade_id: "TRADE-001".to_string(),
    };

    assert_eq!(trade.symbol, symbol);
    assert_eq!(trade.price, price);
    assert_eq!(trade.quantity, quantity);
    assert_eq!(trade.side, OrderSide::Buy);
    assert_eq!(trade.timestamp, timestamp);
    assert_eq!(trade.trade_id, "TRADE-001");
}

#[test]
fn test_trade_event_serialization() {
    let symbol = Symbol::new("AAPL".to_string());
    let price = Price::from_f64(150.50).unwrap();
    let quantity = Quantity::from_f64(100.0).unwrap();
    let timestamp = Utc::now();

    let trade = TradeEvent {
        symbol: symbol.clone(),
        price,
        quantity,
        side: OrderSide::Sell,
        timestamp,
        trade_id: "TRADE-002".to_string(),
    };

    let json = serde_json::to_string(&trade).expect("Failed to serialize");
    let deserialized: TradeEvent = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(deserialized.symbol, symbol);
    assert_eq!(deserialized.price, price);
    assert_eq!(deserialized.side, OrderSide::Sell);
}

// ============================================================================
// QuoteEvent Tests
// ============================================================================

#[test]
fn test_quote_event_creation() {
    let symbol = Symbol::new("GOOGL".to_string());
    let bid_price = Price::from_f64(2800.00).unwrap();
    let bid_quantity = Quantity::from_f64(50.0).unwrap();
    let ask_price = Price::from_f64(2800.50).unwrap();
    let ask_quantity = Quantity::from_f64(75.0).unwrap();
    let timestamp = Utc::now();

    let quote = QuoteEvent {
        symbol: symbol.clone(),
        bid_price,
        bid_quantity,
        ask_price,
        ask_quantity,
        timestamp,
    };

    assert_eq!(quote.symbol, symbol);
    assert_eq!(quote.bid_price, bid_price);
    assert_eq!(quote.bid_quantity, bid_quantity);
    assert_eq!(quote.ask_price, ask_price);
    assert_eq!(quote.ask_quantity, ask_quantity);
}

#[test]
fn test_quote_event_spread() {
    let symbol = Symbol::new("MSFT".to_string());
    let bid_price = Price::from_f64(300.00).unwrap();
    let bid_quantity = Quantity::from_f64(100.0).unwrap();
    let ask_price = Price::from_f64(300.10).unwrap();
    let ask_quantity = Quantity::from_f64(100.0).unwrap();
    let timestamp = Utc::now();

    let quote = QuoteEvent {
        symbol,
        bid_price,
        bid_quantity,
        ask_price,
        ask_quantity,
        timestamp,
    };

    // Spread should be 0.10
    let spread = quote.ask_price - quote.bid_price;
    assert_eq!(spread, Price::from_f64(0.10).unwrap());
}

#[test]
fn test_quote_event_serialization() {
    let symbol = Symbol::new("TSLA".to_string());
    let quote = QuoteEvent {
        symbol: symbol.clone(),
        bid_price: Price::from_f64(200.00).unwrap(),
        bid_quantity: Quantity::from_f64(10.0).unwrap(),
        ask_price: Price::from_f64(200.05).unwrap(),
        ask_quantity: Quantity::from_f64(15.0).unwrap(),
        timestamp: Utc::now(),
    };

    let json = serde_json::to_string(&quote).expect("Failed to serialize");
    let deserialized: QuoteEvent = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(deserialized.symbol, symbol);
    assert_eq!(deserialized.bid_price, quote.bid_price);
    assert_eq!(deserialized.ask_price, quote.ask_price);
}

// ============================================================================
// BarEvent Tests
// ============================================================================

#[test]
fn test_bar_event_creation() {
    let symbol = Symbol::new("SPY".to_string());
    let open = Price::from_f64(450.00).unwrap();
    let high = Price::from_f64(451.50).unwrap();
    let low = Price::from_f64(449.50).unwrap();
    let close = Price::from_f64(450.75).unwrap();
    let volume = Quantity::from_f64(1000000.0).unwrap();
    let timestamp = Utc::now();

    let bar = BarEvent {
        symbol: symbol.clone(),
        open,
        high,
        low,
        close,
        volume,
        timestamp,
        interval: BarInterval::Minute1,
    };

    assert_eq!(bar.symbol, symbol);
    assert_eq!(bar.open, open);
    assert_eq!(bar.high, high);
    assert_eq!(bar.low, low);
    assert_eq!(bar.close, close);
    assert_eq!(bar.volume, volume);
}

#[test]
fn test_bar_interval_variants() {
    let intervals = vec![
        BarInterval::Second1,
        BarInterval::Minute1,
        BarInterval::Minute5,
        BarInterval::Minute15,
        BarInterval::Hour1,
        BarInterval::Day1,
    ];

    for interval in intervals {
        // Test Debug formatting
        let debug_str = format!("{:?}", interval);
        assert!(!debug_str.is_empty());

        // Test serialization
        let json = serde_json::to_string(&interval).expect("Failed to serialize");
        let deserialized: BarInterval = serde_json::from_str(&json).expect("Failed to deserialize");

        // BarInterval is Copy, so we can compare directly
        assert_eq!(format!("{:?}", deserialized), format!("{:?}", interval));
    }
}

#[test]
fn test_bar_event_serialization() {
    let bar = BarEvent {
        symbol: Symbol::new("QQQ".to_string()),
        open: Price::from_f64(350.00).unwrap(),
        high: Price::from_f64(351.00).unwrap(),
        low: Price::from_f64(349.50).unwrap(),
        close: Price::from_f64(350.50).unwrap(),
        volume: Quantity::from_f64(500000.0).unwrap(),
        timestamp: Utc::now(),
        interval: BarInterval::Minute5,
    };

    let json = serde_json::to_string(&bar).expect("Failed to serialize");
    let deserialized: BarEvent = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(deserialized.symbol, bar.symbol);
    assert_eq!(deserialized.open, bar.open);
    assert_eq!(deserialized.high, bar.high);
    assert_eq!(deserialized.low, bar.low);
    assert_eq!(deserialized.close, bar.close);
}

// ============================================================================
// OrderBookEvent Tests
// ============================================================================

#[test]
fn test_order_book_event_creation() {
    let symbol = Symbol::new("BTC-USD".to_string());
    let bids = vec![
        (
            Price::from_f64(50000.00).unwrap(),
            Quantity::from_f64(0.5).unwrap(),
        ),
        (
            Price::from_f64(49999.00).unwrap(),
            Quantity::from_f64(1.0).unwrap(),
        ),
        (
            Price::from_f64(49998.00).unwrap(),
            Quantity::from_f64(2.0).unwrap(),
        ),
    ];
    let asks = vec![
        (
            Price::from_f64(50001.00).unwrap(),
            Quantity::from_f64(0.5).unwrap(),
        ),
        (
            Price::from_f64(50002.00).unwrap(),
            Quantity::from_f64(1.0).unwrap(),
        ),
        (
            Price::from_f64(50003.00).unwrap(),
            Quantity::from_f64(2.0).unwrap(),
        ),
    ];
    let timestamp = Utc::now();

    let order_book = OrderBookEvent {
        symbol: symbol.clone(),
        bids: bids.clone(),
        asks: asks.clone(),
        timestamp,
    };

    assert_eq!(order_book.symbol, symbol);
    assert_eq!(order_book.bids.len(), 3);
    assert_eq!(order_book.asks.len(), 3);
    assert_eq!(order_book.bids[0].0, Price::from_f64(50000.00).unwrap());
}

#[test]
fn test_order_book_event_empty_levels() {
    let symbol = Symbol::new("ETH-USD".to_string());
    let order_book = OrderBookEvent {
        symbol: symbol.clone(),
        bids: vec![],
        asks: vec![],
        timestamp: Utc::now(),
    };

    assert_eq!(order_book.symbol, symbol);
    assert!(order_book.bids.is_empty());
    assert!(order_book.asks.is_empty());
}

#[test]
fn test_order_book_event_serialization() {
    let order_book = OrderBookEvent {
        symbol: Symbol::new("EUR-USD".to_string()),
        bids: vec![(
            Price::from_f64(1.1000).unwrap(),
            Quantity::from_f64(100000.0).unwrap(),
        )],
        asks: vec![(
            Price::from_f64(1.1001).unwrap(),
            Quantity::from_f64(100000.0).unwrap(),
        )],
        timestamp: Utc::now(),
    };

    let json = serde_json::to_string(&order_book).expect("Failed to serialize");
    let deserialized: OrderBookEvent = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(deserialized.symbol, order_book.symbol);
    assert_eq!(deserialized.bids.len(), 1);
    assert_eq!(deserialized.asks.len(), 1);
}

// ============================================================================
// NewsEvent Tests
// ============================================================================

#[test]
fn test_news_event_creation() {
    let symbol = Symbol::new("AAPL".to_string());
    let timestamp = Utc::now();

    let news = NewsEvent {
        symbol: Some(symbol.clone()),
        headline: "Apple announces new product".to_string(),
        content: "Apple Inc. announced today...".to_string(),
        timestamp,
        source: "Bloomberg".to_string(),
    };

    assert_eq!(news.symbol, Some(symbol));
    assert_eq!(news.headline, "Apple announces new product");
    assert!(news.content.contains("Apple Inc."));
    assert_eq!(news.source, "Bloomberg");
}

#[test]
fn test_news_event_no_symbol() {
    let news = NewsEvent {
        symbol: None,
        headline: "Market-wide news".to_string(),
        content: "General market commentary".to_string(),
        timestamp: Utc::now(),
        source: "Reuters".to_string(),
    };

    assert!(news.symbol.is_none());
    assert_eq!(news.headline, "Market-wide news");
}

#[test]
fn test_news_event_serialization() {
    let news = NewsEvent {
        symbol: Some(Symbol::new("TSLA".to_string())),
        headline: "Electric vehicle sales surge".to_string(),
        content: "Tesla reports record deliveries".to_string(),
        timestamp: Utc::now(),
        source: "CNBC".to_string(),
    };

    let json = serde_json::to_string(&news).expect("Failed to serialize");
    let deserialized: NewsEvent = serde_json::from_str(&json).expect("Failed to deserialize");

    assert_eq!(deserialized.symbol, news.symbol);
    assert_eq!(deserialized.headline, news.headline);
    assert_eq!(deserialized.source, news.source);
}

// ============================================================================
// MarketDataEvent Tests
// ============================================================================

#[test]
fn test_market_data_event_trade_variant() {
    let trade = TradeEvent {
        symbol: Symbol::new("AAPL".to_string()),
        price: Price::from_f64(150.00).unwrap(),
        quantity: Quantity::from_f64(100.0).unwrap(),
        side: OrderSide::Buy,
        timestamp: Utc::now(),
        trade_id: "T1".to_string(),
    };

    let event = MarketDataEvent::Trade(trade.clone());

    if let MarketDataEvent::Trade(t) = event {
        assert_eq!(t.symbol, trade.symbol);
        assert_eq!(t.price, trade.price);
    } else {
        panic!("Expected Trade variant");
    }
}

#[test]
fn test_market_data_event_quote_variant() {
    let quote = QuoteEvent {
        symbol: Symbol::new("GOOGL".to_string()),
        bid_price: Price::from_f64(2800.00).unwrap(),
        bid_quantity: Quantity::from_f64(50.0).unwrap(),
        ask_price: Price::from_f64(2800.50).unwrap(),
        ask_quantity: Quantity::from_f64(75.0).unwrap(),
        timestamp: Utc::now(),
    };

    let event = MarketDataEvent::Quote(quote.clone());

    if let MarketDataEvent::Quote(q) = event {
        assert_eq!(q.symbol, quote.symbol);
        assert_eq!(q.bid_price, quote.bid_price);
    } else {
        panic!("Expected Quote variant");
    }
}

#[test]
fn test_market_data_event_bar_variant() {
    let bar = BarEvent {
        symbol: Symbol::new("SPY".to_string()),
        open: Price::from_f64(450.00).unwrap(),
        high: Price::from_f64(451.00).unwrap(),
        low: Price::from_f64(449.00).unwrap(),
        close: Price::from_f64(450.50).unwrap(),
        volume: Quantity::from_f64(1000000.0).unwrap(),
        timestamp: Utc::now(),
        interval: BarInterval::Minute1,
    };

    let event = MarketDataEvent::Bar(bar.clone());

    if let MarketDataEvent::Bar(b) = event {
        assert_eq!(b.symbol, bar.symbol);
        assert_eq!(b.open, bar.open);
    } else {
        panic!("Expected Bar variant");
    }
}

#[test]
fn test_market_data_event_order_book_variant() {
    let order_book = OrderBookEvent {
        symbol: Symbol::new("BTC-USD".to_string()),
        bids: vec![(
            Price::from_f64(50000.00).unwrap(),
            Quantity::from_f64(1.0).unwrap(),
        )],
        asks: vec![(
            Price::from_f64(50001.00).unwrap(),
            Quantity::from_f64(1.0).unwrap(),
        )],
        timestamp: Utc::now(),
    };

    let event = MarketDataEvent::OrderBook(order_book.clone());

    if let MarketDataEvent::OrderBook(ob) = event {
        assert_eq!(ob.symbol, order_book.symbol);
        assert_eq!(ob.bids.len(), 1);
    } else {
        panic!("Expected OrderBook variant");
    }
}

#[test]
fn test_market_data_event_news_variant() {
    let news = NewsEvent {
        symbol: Some(Symbol::new("AAPL".to_string())),
        headline: "Breaking news".to_string(),
        content: "Content here".to_string(),
        timestamp: Utc::now(),
        source: "Bloomberg".to_string(),
    };

    let event = MarketDataEvent::News(news.clone());

    if let MarketDataEvent::News(n) = event {
        assert_eq!(n.symbol, news.symbol);
        assert_eq!(n.headline, news.headline);
    } else {
        panic!("Expected News variant");
    }
}

#[test]
fn test_market_data_event_timestamp_extraction_trade() {
    let timestamp = Utc::now();
    let trade = TradeEvent {
        symbol: Symbol::new("AAPL".to_string()),
        price: Price::from_f64(150.00).unwrap(),
        quantity: Quantity::from_f64(100.0).unwrap(),
        side: OrderSide::Buy,
        timestamp,
        trade_id: "T1".to_string(),
    };

    let event = MarketDataEvent::Trade(trade);
    assert_eq!(event.timestamp(), Some(timestamp));
}

#[test]
fn test_market_data_event_timestamp_extraction_quote() {
    let timestamp = Utc::now();
    let quote = QuoteEvent {
        symbol: Symbol::new("GOOGL".to_string()),
        bid_price: Price::from_f64(2800.00).unwrap(),
        bid_quantity: Quantity::from_f64(50.0).unwrap(),
        ask_price: Price::from_f64(2800.50).unwrap(),
        ask_quantity: Quantity::from_f64(75.0).unwrap(),
        timestamp,
    };

    let event = MarketDataEvent::Quote(quote);
    assert_eq!(event.timestamp(), Some(timestamp));
}

#[test]
fn test_market_data_event_timestamp_extraction_bar() {
    let timestamp = Utc::now();
    let bar = BarEvent {
        symbol: Symbol::new("SPY".to_string()),
        open: Price::from_f64(450.00).unwrap(),
        high: Price::from_f64(451.00).unwrap(),
        low: Price::from_f64(449.00).unwrap(),
        close: Price::from_f64(450.50).unwrap(),
        volume: Quantity::from_f64(1000000.0).unwrap(),
        timestamp,
        interval: BarInterval::Minute1,
    };

    let event = MarketDataEvent::Bar(bar);
    assert_eq!(event.timestamp(), Some(timestamp));
}

#[test]
fn test_market_data_event_timestamp_extraction_order_book() {
    let timestamp = Utc::now();
    let order_book = OrderBookEvent {
        symbol: Symbol::new("BTC-USD".to_string()),
        bids: vec![],
        asks: vec![],
        timestamp,
    };

    let event = MarketDataEvent::OrderBook(order_book);
    assert_eq!(event.timestamp(), Some(timestamp));
}

#[test]
fn test_market_data_event_timestamp_extraction_news() {
    let timestamp = Utc::now();
    let news = NewsEvent {
        symbol: None,
        headline: "News".to_string(),
        content: "Content".to_string(),
        timestamp,
        source: "Source".to_string(),
    };

    let event = MarketDataEvent::News(news);
    assert_eq!(event.timestamp(), Some(timestamp));
}

#[test]
fn test_market_data_event_serialization() {
    let trade = TradeEvent {
        symbol: Symbol::new("AAPL".to_string()),
        price: Price::from_f64(150.00).unwrap(),
        quantity: Quantity::from_f64(100.0).unwrap(),
        side: OrderSide::Buy,
        timestamp: Utc::now(),
        trade_id: "T1".to_string(),
    };

    let event = MarketDataEvent::Trade(trade);

    let json = serde_json::to_string(&event).expect("Failed to serialize");
    let deserialized: MarketDataEvent = serde_json::from_str(&json).expect("Failed to deserialize");

    if let MarketDataEvent::Trade(t) = deserialized {
        assert_eq!(t.symbol, Symbol::new("AAPL".to_string()));
    } else {
        panic!("Expected Trade variant after deserialization");
    }
}
