//! Comprehensive Data Normalization Tests
//!
//! Tests for market data transformation, normalization, and standardization.

#![allow(
    clippy::absurd_extreme_comparisons,
    clippy::assertions_on_constants,
    clippy::assertions_on_result_states,
    clippy::clone_on_copy,
    clippy::decimal_literal_representation,
    clippy::doc_lazy_continuation,
    clippy::doc_markdown,
    clippy::double_comparisons,
    clippy::else_if_without_else,
    clippy::empty_drop,
    clippy::expect_used,
    clippy::field_reassign_with_default,
    clippy::format_push_string,
    clippy::if_then_some_else_none,
    clippy::indexing_slicing,
    clippy::integer_division,
    clippy::len_zero,
    clippy::let_and_return,
    clippy::let_underscore_must_use,
    clippy::manual_let_else,
    clippy::manual_range_contains,
    clippy::map_err_ignore,
    clippy::missing_const_for_fn,
    clippy::modulo_arithmetic,
    clippy::needless_range_loop,
    clippy::new_without_default,
    clippy::non_ascii_literal,
    clippy::nonminimal_bool,
    clippy::octal_escapes,
    clippy::overly_complex_bool_expr,
    clippy::redundant_clone,
    clippy::shadow_reuse,
    clippy::shadow_unrelated,
    clippy::similar_names,
    clippy::single_component_path_imports,
    clippy::single_match_else,
    clippy::str_to_string,
    clippy::string_slice,
    clippy::tests_outside_test_module,
    clippy::unnecessary_cast,
    clippy::unnecessary_get_then_check,
    clippy::unnecessary_unwrap,
    clippy::unseparated_literal_suffix,
    clippy::unwrap_or_default,
    clippy::unwrap_used,
    clippy::use_debug,
    clippy::useless_vec,
    clippy::wildcard_enum_match_arm,
    dead_code,
    deprecated,
    unreachable_pub,
    unused_assignments,
    unused_comparisons,
    unused_imports,
    unused_variables
)]

use chrono::Utc;
use common::{MarketDataEvent, QuoteEvent, Symbol, TradeEvent};
use rust_decimal::Decimal;
use rust_decimal_macros::dec;

#[tokio::test]
async fn test_trade_event_normalization() {
    let trade = TradeEvent {
        symbol: "AAPL".to_string(),
        price: dec!(150.25),
        size: dec!(100),
        timestamp: Utc::now(),
        trade_id: Some("TRADE-001".to_string()),
        exchange: Some("NYSE".to_string()),
        conditions: vec!["REGULAR".to_string()],
        sequence: 1,
    };

    assert_eq!(trade.symbol, "AAPL".to_string());
    assert_eq!(trade.price, dec!(150.25));
    assert_eq!(trade.size, dec!(100));
    assert!(trade.trade_id.is_some());
}

#[tokio::test]
async fn test_quote_event_normalization() {
    let quote = QuoteEvent {
        symbol: "MSFT".to_string(),
        bid: Some(dec!(380.50)),
        ask: Some(dec!(380.55)),
        bid_size: Some(dec!(200)),
        ask_size: Some(dec!(150)),
        timestamp: Utc::now(),
        exchange: Some("NASDAQ".to_string()),
        bid_exchange: Some("NASDAQ".to_string()),
        ask_exchange: Some("NASDAQ".to_string()),
        conditions: vec![],
        sequence: 2,
    };

    assert_eq!(quote.symbol, "MSFT".to_string());
    assert!(quote.bid.is_some());
    assert!(quote.ask.is_some());
    assert!(quote.bid.unwrap() < quote.ask.unwrap());
}

#[tokio::test]
async fn test_price_normalization_to_decimal() {
    let prices = vec![
        (100.0, dec!(100)),
        (150.25, dec!(150.25)),
        (0.01, dec!(0.01)),
        (999999.99, dec!(999999.99)),
    ];

    for (float_price, expected_decimal) in prices {
        let decimal_price = Decimal::try_from(float_price).unwrap();
        assert_eq!(decimal_price, expected_decimal);
    }
}

#[tokio::test]
async fn test_volume_normalization() {
    let volumes = vec![(1, dec!(1)), (100, dec!(100)), (1000000, dec!(1000000))];

    for (int_volume, expected_decimal) in volumes {
        let decimal_volume = Decimal::from(int_volume);
        assert_eq!(decimal_volume, expected_decimal);
    }
}

#[tokio::test]
async fn test_symbol_normalization() {
    let symbol_strings = vec![
        ("AAPL", "AAPL"),
        ("aapl", "AAPL"),
        ("SPY", "SPY"),
        ("QQQ", "QQQ"),
    ];

    for (input, expected) in symbol_strings {
        let symbol = Symbol::from(input);
        assert_eq!(symbol.to_string().to_uppercase(), expected);
    }
}

#[tokio::test]
async fn test_exchange_code_normalization() {
    let exchanges = vec!["NYSE", "NASDAQ", "AMEX", "ARCA", "BATS"];

    for exchange in exchanges {
        let trade = TradeEvent {
            symbol: "TEST".to_string(),
            price: dec!(100),
            size: dec!(100),
            timestamp: Utc::now(),
            trade_id: None,
            exchange: Some(exchange.to_string()),
            conditions: vec![],
            sequence: 1,
        };

        assert_eq!(trade.exchange.unwrap(), exchange);
    }
}

#[tokio::test]
async fn test_trade_conditions_normalization() {
    let conditions = vec![
        vec!["REGULAR"],
        vec!["OPENING", "REGULAR"],
        vec!["CLOSING"],
        vec!["ODD_LOT"],
        vec!["INTERMARKET_SWEEP"],
    ];

    for conds in conditions {
        let trade = TradeEvent {
            symbol: "TEST".to_string(),
            price: dec!(100),
            size: dec!(100),
            timestamp: Utc::now(),
            trade_id: None,
            exchange: None,
            conditions: conds.iter().map(|s| s.to_string()).collect(),
            sequence: 1,
        };

        assert_eq!(trade.conditions.len(), conds.len());
    }
}

#[tokio::test]
async fn test_timestamp_normalization() {
    let now = Utc::now();

    let trade = TradeEvent {
        symbol: "TEST".to_string(),
        price: dec!(100),
        size: dec!(100),
        timestamp: now,
        trade_id: None,
        exchange: None,
        conditions: vec![],
        sequence: 1,
    };

    assert_eq!(trade.timestamp, now);
}

#[tokio::test]
async fn test_bid_ask_spread_calculation() {
    let quote = QuoteEvent {
        symbol: "TEST".to_string(),
        bid: Some(dec!(100.00)),
        ask: Some(dec!(100.05)),
        bid_size: Some(dec!(100)),
        ask_size: Some(dec!(100)),
        timestamp: Utc::now(),
        exchange: None,
        bid_exchange: None,
        ask_exchange: None,
        conditions: vec![],
        sequence: 1,
    };

    let spread = quote.ask.unwrap() - quote.bid.unwrap();
    assert_eq!(spread, dec!(0.05));
}

#[tokio::test]
async fn test_midpoint_price_calculation() {
    let quote = QuoteEvent {
        symbol: "TEST".to_string(),
        bid: Some(dec!(100.00)),
        ask: Some(dec!(100.10)),
        bid_size: Some(dec!(100)),
        ask_size: Some(dec!(100)),
        timestamp: Utc::now(),
        exchange: None,
        bid_exchange: None,
        ask_exchange: None,
        conditions: vec![],
        sequence: 1,
    };

    let midpoint = (quote.bid.unwrap() + quote.ask.unwrap()) / dec!(2);
    assert_eq!(midpoint, dec!(100.05));
}

#[tokio::test]
async fn test_zero_price_handling() {
    let trade = TradeEvent {
        symbol: "TEST".to_string(),
        price: dec!(0),
        size: dec!(100),
        timestamp: Utc::now(),
        trade_id: None,
        exchange: None,
        conditions: vec![],
        sequence: 1,
    };

    assert_eq!(trade.price, dec!(0));
    assert!(trade.price >= dec!(0));
}

#[tokio::test]
async fn test_negative_price_representation() {
    // While negative prices are invalid, test that Decimal can represent them
    let price = dec!(-10.5);
    assert!(price < dec!(0));
    assert_eq!(price.abs(), dec!(10.5));
}

#[tokio::test]
async fn test_very_small_prices() {
    let small_prices = vec![dec!(0.0001), dec!(0.00001), dec!(0.000001)];

    for price in small_prices {
        assert!(price > dec!(0));
        assert!(price < dec!(1));
    }
}

#[tokio::test]
async fn test_very_large_prices() {
    let large_prices = vec![dec!(100000), dec!(1000000), dec!(10000000)];

    for price in large_prices {
        assert!(price > dec!(10000));
        assert!(price >= dec!(100000));
    }
}

#[tokio::test]
async fn test_decimal_precision() {
    let price = dec!(123.456789);

    // Decimal maintains precision
    assert!(price > dec!(123.456));
    assert!(price < dec!(123.457));
}

#[tokio::test]
async fn test_size_normalization_zero() {
    let trade = TradeEvent {
        symbol: "TEST".to_string(),
        price: dec!(100),
        size: dec!(0),
        timestamp: Utc::now(),
        trade_id: None,
        exchange: None,
        conditions: vec![],
        sequence: 1,
    };

    assert_eq!(trade.size, dec!(0));
}

#[tokio::test]
async fn test_fractional_shares() {
    let trade = TradeEvent {
        symbol: "TEST".to_string(),
        price: dec!(100),
        size: dec!(0.5),
        timestamp: Utc::now(),
        trade_id: None,
        exchange: None,
        conditions: vec![],
        sequence: 1,
    };

    assert_eq!(trade.size, dec!(0.5));
    assert!(trade.size > dec!(0));
    assert!(trade.size < dec!(1));
}

#[tokio::test]
async fn test_market_data_event_variants() {
    let trade = MarketDataEvent::Trade(TradeEvent {
        symbol: "TEST".to_string(),
        price: dec!(100),
        size: dec!(100),
        timestamp: Utc::now(),
        trade_id: None,
        exchange: None,
        conditions: vec![],
        sequence: 1,
    });

    let quote = MarketDataEvent::Quote(QuoteEvent {
        symbol: "TEST".to_string(),
        bid: Some(dec!(100)),
        ask: Some(dec!(101)),
        bid_size: Some(dec!(100)),
        ask_size: Some(dec!(100)),
        timestamp: Utc::now(),
        exchange: None,
        bid_exchange: None,
        ask_exchange: None,
        conditions: vec![],
        sequence: 1,
    });

    assert!(matches!(trade, MarketDataEvent::Trade(_)));
    assert!(matches!(quote, MarketDataEvent::Quote(_)));
}

#[tokio::test]
async fn test_sequence_number_normalization() {
    let events = vec![
        TradeEvent {
            symbol: "TEST".to_string(),
            price: dec!(100),
            size: dec!(100),
            timestamp: Utc::now(),
            trade_id: None,
            exchange: None,
            conditions: vec![],
            sequence: 1,
        },
        TradeEvent {
            symbol: "TEST".to_string(),
            price: dec!(100),
            size: dec!(100),
            timestamp: Utc::now(),
            trade_id: None,
            exchange: None,
            conditions: vec![],
            sequence: 2,
        },
    ];

    assert_eq!(events[0].sequence, 1);
    assert_eq!(events[1].sequence, 2);
    assert!(events[1].sequence > events[0].sequence);
}

#[tokio::test]
async fn test_trade_id_normalization() {
    let trade_ids = vec![
        Some("TRADE-001".to_string()),
        Some("12345".to_string()),
        Some("ABC-XYZ-789".to_string()),
        None,
    ];

    for trade_id in trade_ids {
        let trade = TradeEvent {
            symbol: "TEST".to_string(),
            price: dec!(100),
            size: dec!(100),
            timestamp: Utc::now(),
            trade_id: trade_id.clone(),
            exchange: None,
            conditions: vec![],
            sequence: 1,
        };

        assert_eq!(trade.trade_id, trade_id);
    }
}

#[tokio::test]
async fn test_multiple_exchange_quotes() {
    let quote = QuoteEvent {
        symbol: "TEST".to_string(),
        bid: Some(dec!(100.00)),
        ask: Some(dec!(100.05)),
        bid_size: Some(dec!(200)),
        ask_size: Some(dec!(150)),
        timestamp: Utc::now(),
        exchange: Some("CONSOLIDATED".to_string()),
        bid_exchange: Some("NYSE".to_string()),
        ask_exchange: Some("NASDAQ".to_string()),
        conditions: vec![],
        sequence: 1,
    };

    assert_eq!(quote.exchange.unwrap(), "CONSOLIDATED");
    assert_eq!(quote.bid_exchange.unwrap(), "NYSE");
    assert_eq!(quote.ask_exchange.unwrap(), "NASDAQ");
}

#[tokio::test]
async fn test_quote_without_sizes() {
    let quote = QuoteEvent {
        symbol: "TEST".to_string(),
        bid: Some(dec!(100)),
        ask: Some(dec!(101)),
        bid_size: None,
        ask_size: None,
        timestamp: Utc::now(),
        exchange: None,
        bid_exchange: None,
        ask_exchange: None,
        conditions: vec![],
        sequence: 1,
    };

    assert!(quote.bid_size.is_none());
    assert!(quote.ask_size.is_none());
}

#[tokio::test]
async fn test_quote_with_missing_prices() {
    let quote = QuoteEvent {
        symbol: "TEST".to_string(),
        bid: None,
        ask: None,
        bid_size: Some(dec!(100)),
        ask_size: Some(dec!(100)),
        timestamp: Utc::now(),
        exchange: None,
        bid_exchange: None,
        ask_exchange: None,
        conditions: vec![],
        sequence: 1,
    };

    assert!(quote.bid.is_none());
    assert!(quote.ask.is_none());
}

#[tokio::test]
async fn test_price_arithmetic_precision() {
    let price1 = dec!(100.123);
    let price2 = dec!(50.456);

    let sum = price1 + price2;
    let diff = price1 - price2;
    let product = price1 * price2;

    assert_eq!(sum, dec!(150.579));
    assert_eq!(diff, dec!(49.667));
    assert!(product > dec!(5000));
}

#[tokio::test]
async fn test_volume_weighted_average_price() {
    let trades = vec![
        (dec!(100), dec!(100)), // price, size
        (dec!(101), dec!(200)),
        (dec!(99), dec!(150)),
    ];

    let total_value: Decimal = trades.iter().map(|(price, size)| price * size).sum();

    let total_volume: Decimal = trades.iter().map(|(_, size)| size).sum();

    let vwap = total_value / total_volume;

    assert!(vwap > dec!(99));
    assert!(vwap < dec!(101));
}

#[tokio::test]
async fn test_percentage_change_calculation() {
    let old_price = dec!(100);
    let new_price = dec!(105);

    let change = new_price - old_price;
    let pct_change = (change / old_price) * dec!(100);

    assert_eq!(pct_change, dec!(5));
}

#[tokio::test]
async fn test_tick_size_normalization() {
    let tick_sizes = vec![
        dec!(0.01), // Penny tick
        dec!(0.05), // Nickel tick
        dec!(0.10), // Dime tick
        dec!(0.25), // Quarter tick
    ];

    for tick_size in tick_sizes {
        assert!(tick_size > dec!(0));
        assert!(tick_size < dec!(1));
    }
}

#[tokio::test]
async fn test_round_lot_normalization() {
    let lot_sizes = vec![
        dec!(100), // Standard round lot
        dec!(10),  // Small round lot
        dec!(1),   // Odd lot
    ];

    for lot_size in lot_sizes {
        assert!(lot_size > dec!(0));
        assert_eq!(lot_size % dec!(1), dec!(0)); // Whole number
    }
}

#[tokio::test]
async fn test_data_type_conversion_safety() {
    // Test that conversions between types are safe
    let float_price: f64 = 123.45;
    let decimal_price = Decimal::try_from(float_price).unwrap();

    assert!(decimal_price > dec!(123));
    assert!(decimal_price < dec!(124));
}

#[tokio::test]
async fn test_cross_exchange_price_comparison() {
    let nyse_quote = QuoteEvent {
        symbol: "AAPL".to_string(),
        bid: Some(dec!(150.00)),
        ask: Some(dec!(150.05)),
        bid_size: Some(dec!(100)),
        ask_size: Some(dec!(100)),
        timestamp: Utc::now(),
        exchange: Some("NYSE".to_string()),
        bid_exchange: Some("NYSE".to_string()),
        ask_exchange: Some("NYSE".to_string()),
        conditions: vec![],
        sequence: 1,
    };

    let nasdaq_quote = QuoteEvent {
        symbol: "AAPL".to_string(),
        bid: Some(dec!(150.01)),
        ask: Some(dec!(150.04)),
        bid_size: Some(dec!(200)),
        ask_size: Some(dec!(150)),
        timestamp: Utc::now(),
        exchange: Some("NASDAQ".to_string()),
        bid_exchange: Some("NASDAQ".to_string()),
        ask_exchange: Some("NASDAQ".to_string()),
        conditions: vec![],
        sequence: 2,
    };

    // Compare NBBO
    let best_bid = nyse_quote.bid.unwrap().max(nasdaq_quote.bid.unwrap());
    let best_ask = nyse_quote.ask.unwrap().min(nasdaq_quote.ask.unwrap());

    assert_eq!(best_bid, dec!(150.01)); // NASDAQ has better bid
    assert_eq!(best_ask, dec!(150.04)); // NASDAQ has better ask
}

#[tokio::test]
async fn test_market_data_event_timestamp_access() {
    let trade_event = MarketDataEvent::Trade(TradeEvent {
        symbol: "TEST".to_string(),
        price: dec!(100),
        size: dec!(100),
        timestamp: Utc::now(),
        trade_id: None,
        exchange: None,
        conditions: vec![],
        sequence: 1,
    });

    let quote_event = MarketDataEvent::Quote(QuoteEvent {
        symbol: "TEST".to_string(),
        bid: Some(dec!(100)),
        ask: Some(dec!(101)),
        bid_size: Some(dec!(100)),
        ask_size: Some(dec!(100)),
        timestamp: Utc::now(),
        exchange: None,
        bid_exchange: None,
        ask_exchange: None,
        conditions: vec![],
        sequence: 1,
    });

    // Both should have accessible timestamps
    assert!(trade_event.timestamp() <= Some(Utc::now()));
    assert!(quote_event.timestamp() <= Some(Utc::now()));
}

#[tokio::test]
async fn test_market_data_event_symbol_access() {
    let event = MarketDataEvent::Trade(TradeEvent {
        symbol: "AAPL".to_string(),
        price: dec!(150),
        size: dec!(100),
        timestamp: Utc::now(),
        trade_id: None,
        exchange: None,
        conditions: vec![],
        sequence: 1,
    });

    assert_eq!(event.symbol(), Symbol::from("AAPL"));
}
