//! Market data test fixtures
//!
//! Provides mock OHLCV bars, ticks, and market data generators.
//! Consolidates patterns from 15+ test files with `create_mock_bars()` functions.

use chrono::{DateTime, Duration, Utc};
use ml::data_loader::OHLCVBar;
use rand::Rng;

/// Generate OHLCV bars with realistic price movements
///
/// Pattern found in:
/// - ml/tests/feature_cache_tests.rs
/// - ml/tests/dqn_feature_cache_test.rs
/// - trading_engine/src/features/mod.rs
pub fn generate_ohlcv_bars(count: usize, start_price: f64) -> Vec<OHLCVBar> {
    let mut bars = Vec::with_capacity(count);
    let mut current_price = start_price;
    let base_time = Utc::now() - Duration::minutes((count * 5) as i64);

    for i in 0..count {
        // Generate realistic price movements
        let change = ((i as f64 / 100.0).sin() * 0.02) + (rand::thread_rng().gen::<f64>() - 0.5) * 0.01;
        current_price *= 1.0 + change;

        let high = current_price * (1.0 + rand::thread_rng().gen::<f64>() * 0.005);
        let low = current_price * (1.0 - rand::thread_rng().gen::<f64>() * 0.005);
        let close = current_price * (1.0 + (rand::thread_rng().gen::<f64>() - 0.5) * 0.003);

        bars.push(OHLCVBar {
            timestamp: base_time + Duration::minutes((i * 5) as i64),
            open: current_price,
            high,
            low,
            close,
            volume: 1000.0 + rand::thread_rng().gen::<f64>() * 5000.0,
        });

        current_price = close;
    }

    bars
}

/// Generate random walk price series
///
/// Used for Monte Carlo simulations and VaR testing.
pub fn generate_random_walk(count: usize, volatility: f64) -> Vec<f64> {
    let mut prices = Vec::with_capacity(count);
    let mut current_price = 100.0;

    for _ in 0..count {
        let change = (rand::thread_rng().gen::<f64>() - 0.5) * volatility;
        current_price *= 1.0 + change;
        prices.push(current_price);
    }

    prices
}

/// Generate crisis scenario returns (2008 Financial Crisis pattern)
///
/// Pattern from risk/tests/risk_comprehensive_tests.rs
pub fn generate_crisis_returns() -> Vec<f64> {
    vec![
        -0.08, -0.10, -0.07, -0.12, -0.09,
        -0.06, -0.15, -0.11, -0.08, -0.13,
        -0.05, -0.09, -0.14, -0.07, -0.10,
        -0.12, -0.08, -0.06, -0.11, -0.09,
    ]
}

/// Generate COVID crash returns (March 2020 pattern)
///
/// Pattern from risk/tests/risk_comprehensive_tests.rs
pub fn generate_covid_crash_returns() -> Vec<f64> {
    vec![
        -0.12, -0.09, -0.13, -0.08, -0.11,
        -0.06, -0.10, -0.07, -0.09, -0.14,
        -0.05, -0.08, -0.12, -0.09, -0.11,
        -0.13, -0.07, -0.10, -0.06, -0.09,
    ]
}

/// Generate bull market returns
pub fn generate_bull_market_returns() -> Vec<f64> {
    vec![
        0.05, 0.03, 0.07, 0.04, 0.06,
        0.02, 0.08, 0.05, 0.04, 0.06,
        0.03, 0.07, 0.05, 0.04, 0.06,
        0.08, 0.03, 0.05, 0.07, 0.04,
    ]
}

/// Generate mock market ticks
#[derive(Clone, Debug)]
pub struct MockTick {
    pub timestamp: DateTime<Utc>,
    pub price: f64,
    pub volume: f64,
}

pub fn generate_market_ticks(count: usize, start_price: f64) -> Vec<MockTick> {
    let mut ticks = Vec::with_capacity(count);
    let mut current_price = start_price;
    let base_time = Utc::now() - Duration::seconds(count as i64);

    for i in 0..count {
        current_price += (rand::thread_rng().gen::<f64>() - 0.5) * 0.1;

        ticks.push(MockTick {
            timestamp: base_time + Duration::seconds(i as i64),
            price: current_price,
            volume: 10.0 + (rand::thread_rng().gen::<f64>() * 100.0),
        });
    }

    ticks
}

/// Generate order book levels
#[derive(Clone, Debug)]
pub struct OrderBookLevel {
    pub price: f64,
    pub size: f64,
}

pub fn generate_order_book(mid_price: f64, levels: usize) -> (Vec<OrderBookLevel>, Vec<OrderBookLevel>) {
    let mut bids = Vec::with_capacity(levels);
    let mut asks = Vec::with_capacity(levels);

    for i in 0..levels {
        let spread = (i as f64 + 1.0) * 0.01;

        bids.push(OrderBookLevel {
            price: mid_price * (1.0 - spread),
            size: 1000.0 + (rand::thread_rng().gen::<f64>() * 5000.0),
        });

        asks.push(OrderBookLevel {
            price: mid_price * (1.0 + spread),
            size: 1000.0 + (rand::thread_rng().gen::<f64>() * 5000.0),
        });
    }

    (bids, asks)
}

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

    #[test]
    fn test_generate_ohlcv_bars() {
        let bars = generate_ohlcv_bars(100, 150.0);
        assert_eq!(bars.len(), 100);

        // Check OHLC relationships
        for bar in &bars {
            assert!(bar.high >= bar.open);
            assert!(bar.high >= bar.close);
            assert!(bar.low <= bar.open);
            assert!(bar.low <= bar.close);
        }
    }

    #[test]
    fn test_generate_random_walk() {
        let prices = generate_random_walk(100, 0.02);
        assert_eq!(prices.len(), 100);
        assert!(prices[0] > 0.0);
    }

    #[test]
    fn test_crisis_returns() {
        let returns = generate_crisis_returns();
        assert_eq!(returns.len(), 20);
        assert!(returns.iter().all(|&r| r < 0.0));
    }

    #[test]
    fn test_market_ticks() {
        let ticks = generate_market_ticks(100, 150.0);
        assert_eq!(ticks.len(), 100);
    }

    #[test]
    fn test_order_book() {
        let (bids, asks) = generate_order_book(150.0, 10);
        assert_eq!(bids.len(), 10);
        assert_eq!(asks.len(), 10);

        // Bids should be descending
        for i in 0..bids.len() - 1 {
            assert!(bids[i].price > bids[i + 1].price);
        }

        // Asks should be ascending
        for i in 0..asks.len() - 1 {
            assert!(asks[i].price < asks[i + 1].price);
        }
    }
}
