#[cfg(test)]
mod tests {
    use super::super::*;
    use crate::{
        Bar, MarketDataType, MarketUpdate, TradingMode,
        core::{
            time_providers::SimulatedTime,
            market_data_sources::HistoricalDataSource,
            execution_handlers::{SimulatedExecution, BacktestFillSimulator},
        },
    };
    use chrono::{DateTime, Utc, TimeZone};
    use std::collections::HashMap;
    
    // Test strategy that tracks prices and places orders
    struct PriceTrackingStrategy {
        name: String,
        price_history: Vec<(DateTime<Utc>, String, f64)>,
        should_buy_at: Vec<(String, usize)>, // (symbol, bar_index)
        should_sell_at: Vec<(String, usize)>,
        bar_count: HashMap<String, usize>,
    }
    
    impl PriceTrackingStrategy {
        fn new(name: &str) -> Self {
            Self {
                name: name.to_string(),
                price_history: Vec::new(),
                should_buy_at: Vec::new(),
                should_sell_at: Vec::new(),
                bar_count: HashMap::new(),
            }
        }
        
        fn with_trades(mut self, buys: Vec<(String, usize)>, sells: Vec<(String, usize)>) -> Self {
            self.should_buy_at = buys;
            self.should_sell_at = sells;
            self
        }
    }
    
    impl Strategy for PriceTrackingStrategy {
        fn get_name(&self) -> &str {
            &self.name
        }
        
        fn get_parameters(&self) -> serde_json::Value {
            serde_json::json!({})
        }
        
        fn on_market_data(&mut self, data: &MarketUpdate) -> Vec<Signal> {
            let mut signals = Vec::new();
            
            // Track the price
            if let MarketDataType::Bar(bar) = &data.data {
                self.price_history.push((data.timestamp, data.symbol.clone(), bar.close));
                
                let count = self.bar_count.entry(data.symbol.clone()).or_insert(0);
                *count += 1;
                
                // Check if we should trade at this bar
                for (symbol, bar_index) in &self.should_buy_at {
                    if &data.symbol == symbol && *count == *bar_index {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Buy,
                            strength: 1.0,
                            quantity: Some(100.0),
                            reason: Some("Test buy signal".to_string()),
                            metadata: None,
                        });
                    }
                }
                
                for (symbol, bar_index) in &self.should_sell_at {
                    if &data.symbol == symbol && *count == *bar_index {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Sell,
                            strength: 1.0,
                            quantity: Some(100.0),
                            reason: Some("Test sell signal".to_string()),
                            metadata: None,
                        });
                    }
                }
            }
            
            signals
        }
        
        fn on_fill(&mut self, _symbol: &str, _quantity: f64, _price: f64, _side: &str) {
            // Track fills if needed
        }
    }
    
    fn create_test_bars(symbol: &str, start_time: DateTime<Utc>, prices: Vec<f64>) -> Vec<MarketUpdate> {
        prices.into_iter().enumerate().map(|(i, close)| {
            let timestamp = start_time + chrono::Duration::days(i as i64);
            let high = close * 1.01;
            let low = close * 0.99;
            let open = if i == 0 { close } else { close * 0.995 };
            
            MarketUpdate {
                symbol: symbol.to_string(),
                timestamp,
                data: MarketDataType::Bar(Bar {
                    open,
                    high,
                    low,
                    close,
                    volume: 1000000.0,
                    vwap: Some(close),
                }),
            }
        }).collect()
    }
    
    #[tokio::test]
    async fn test_price_tracking_single_symbol() {
        // Create test data with known prices
        let start_time = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
        let end_time = Utc.with_ymd_and_hms(2024, 1, 10, 0, 0, 0).unwrap();
        
        // Prices: index 0=100, 1=101, 2=102, 3=103, 4=104, 5=105, 6=104, 7=103, 8=102, 9=101
        let prices = vec![100.0, 101.0, 102.0, 103.0, 104.0, 105.0, 104.0, 103.0, 102.0, 101.0];
        let bars = create_test_bars("AAPL", start_time, prices.clone());
        
        // Setup backtest engine
        let config = BacktestConfig {
            name: "PriceTrackingTest".to_string(),
            strategy: None,
            start_time,
            end_time,
            initial_capital: 100000.0,
            commission: 0.001,
            slippage: 0.0001,
            symbols: vec!["AAPL".to_string()],
            data_frequency: "1d".to_string(),
        };
        
        let time_provider = Box::new(SimulatedTime::new(start_time));
        let mut data_source = Box::new(HistoricalDataSource::new());
        data_source.load_data(bars.clone());
        
        let fill_simulator = Box::new(BacktestFillSimulator::new());
        let execution_handler = Box::new(SimulatedExecution::new(fill_simulator));
        
        let mode = TradingMode::Backtest {
            start_time,
            end_time,
            speed_multiplier: 1.0,
        };
        
        let mut engine = BacktestEngine::new(
            config,
            mode,
            time_provider,
            data_source,
            execution_handler,
        );
        
        // Add strategy that tracks prices and trades at specific bars
        // Note: bar_count starts at 1, so bar 3 is the 3rd bar (index 2, price 102.0)
        let strategy = PriceTrackingStrategy::new("PriceTracker")
            .with_trades(
                vec![("AAPL".to_string(), 3)], // Buy at bar 3 (3rd bar, price 102)
                vec![("AAPL".to_string(), 7)], // Sell at bar 7 (7th bar, price 103)
            );
        
        let strategy_ref = Box::new(strategy);
        engine.add_strategy(strategy_ref);
        
        // Run backtest
        let result = engine.run().await.unwrap();
        
        // Verify trades executed at correct prices
        assert_eq!(result.trades.len(), 2, "Should have exactly 2 trades");
        
        let buy_trade = &result.trades[0];
        assert_eq!(buy_trade.side, "buy");
        assert_eq!(buy_trade.symbol, "AAPL");
        // Buy signal at bar 3 (index 2, price 102) executes at that bar's close price
        // In backtesting, we execute at the close price of the bar that generated the signal
        let expected_buy_price = 102.0 * (1.0 + 0.0001);
        assert!(
            (buy_trade.price - expected_buy_price).abs() < 0.01,
            "Buy price {} should be close to {}", buy_trade.price, expected_buy_price
        );
        
        let sell_trade = &result.trades[1];
        assert_eq!(sell_trade.side, "sell");
        assert_eq!(sell_trade.symbol, "AAPL");
        // Sell signal at bar 7 (7th bar, index 6, price 104) executes at that bar's close price
        let expected_sell_price = 104.0 * (1.0 - 0.0001);
        assert!(
            (sell_trade.price - expected_sell_price).abs() < 0.01,
            "Sell price {} should be close to {}", sell_trade.price, expected_sell_price
        );
    }
    
    #[tokio::test]
    async fn test_price_tracking_multiple_symbols() {
        let start_time = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
        let end_time = Utc.with_ymd_and_hms(2024, 1, 5, 0, 0, 0).unwrap();
        
        // Create different price series for each symbol
        // AAPL: index 0=100, 1=101, 2=102, 3=103, 4=104
        let aapl_prices = vec![100.0, 101.0, 102.0, 103.0, 104.0];
        // GOOGL: index 0=150, 1=149, 2=148, 3=147, 4=146
        let googl_prices = vec![150.0, 149.0, 148.0, 147.0, 146.0];
        // MSFT: index 0=200, 1=202, 2=204, 3=206, 4=208
        let msft_prices = vec![200.0, 202.0, 204.0, 206.0, 208.0];
        
        let mut all_bars = Vec::new();
        all_bars.extend(create_test_bars("AAPL", start_time, aapl_prices));
        all_bars.extend(create_test_bars("GOOGL", start_time, googl_prices));
        all_bars.extend(create_test_bars("MSFT", start_time, msft_prices));
        
        // Sort by timestamp to simulate realistic data flow
        all_bars.sort_by_key(|bar| bar.timestamp);
        
        // Setup backtest
        let config = BacktestConfig {
            name: "MultiSymbolTest".to_string(),
            strategy: None,
            start_time,
            end_time,
            initial_capital: 100000.0,
            commission: 0.001,
            slippage: 0.0,
            symbols: vec!["AAPL".to_string(), "GOOGL".to_string(), "MSFT".to_string()],
            data_frequency: "1d".to_string(),
        };
        
        let time_provider = Box::new(SimulatedTime::new(start_time));
        let mut data_source = Box::new(HistoricalDataSource::new());
        data_source.load_data(all_bars);
        
        let fill_simulator = Box::new(BacktestFillSimulator::new());
        let execution_handler = Box::new(SimulatedExecution::new(fill_simulator));
        
        let mode = TradingMode::Backtest {
            start_time,
            end_time,
            speed_multiplier: 1.0,
        };
        
        let mut engine = BacktestEngine::new(
            config,
            mode,
            time_provider,
            data_source,
            execution_handler,
        );
        
        // Strategy that trades each symbol at different times
        let strategy = PriceTrackingStrategy::new("MultiSymbolTracker")
            .with_trades(
                vec![
                    ("AAPL".to_string(), 2),   // Buy AAPL at bar 2 (price 102)
                    ("GOOGL".to_string(), 3),  // Buy GOOGL at bar 3 (price 147)
                    ("MSFT".to_string(), 4),   // Buy MSFT at bar 4 (price 208)
                ],
                vec![
                    ("AAPL".to_string(), 4),   // Sell AAPL at bar 4 (price 104)
                    ("GOOGL".to_string(), 5),  // Sell GOOGL at bar 5 (price 146)
                    ("MSFT".to_string(), 5),   // Sell MSFT at bar 5 (price 208)
                ],
            );
        
        engine.add_strategy(Box::new(strategy));
        
        // Run backtest
        let result = engine.run().await.unwrap();
        
        // Verify each symbol traded at its own correct price
        // Note: Trades execute at the close price of the bar when the signal is generated
        let aapl_trades: Vec<_> = result.trades.iter().filter(|t| t.symbol == "AAPL").collect();
        assert_eq!(aapl_trades.len(), 2);
        // Buy at bar 2 (when bar_count=2) = Jan 3 = index 2 = price 102
        assert!((aapl_trades[0].price - 102.0).abs() < 0.1, "AAPL buy price should be ~102 (bar 2)");
        // Sell at bar 4 (when bar_count=4) = Jan 5 = index 4 = price 104
        assert!((aapl_trades[1].price - 104.0).abs() < 0.1, "AAPL sell price should be ~104 (bar 4)");
        
        let googl_trades: Vec<_> = result.trades.iter().filter(|t| t.symbol == "GOOGL").collect();
        assert_eq!(googl_trades.len(), 2);
        // Buy at bar 3 = Jan 4 = index 3 = price 147
        assert!((googl_trades[0].price - 147.0).abs() < 0.1, "GOOGL buy price should be ~147 (bar 3)");
        // Sell at bar 5 or closing = price 146
        assert!((googl_trades[1].price - 146.0).abs() < 0.1, "GOOGL sell price should be ~146 (closing)");
        
        let msft_trades: Vec<_> = result.trades.iter().filter(|t| t.symbol == "MSFT").collect();
        assert_eq!(msft_trades.len(), 2);
        // Buy at bar 4 = Jan 5 = index 4 = price 208 (but on Jan 4 it's 206)
        assert!((msft_trades[0].price - 206.0).abs() < 0.1, "MSFT buy price should be ~206 (bar 4)");
        // Sell at bar 5 = end of data = price 208
        assert!((msft_trades[1].price - 208.0).abs() < 0.1, "MSFT sell price should be ~208 (bar 5)");
    }
    
    #[tokio::test]
    async fn test_closing_position_prices() {
        let start_time = Utc.with_ymd_and_hms(2024, 1, 1, 0, 0, 0).unwrap();
        let end_time = Utc.with_ymd_and_hms(2024, 1, 5, 0, 0, 0).unwrap();
        
        // Prices that change significantly
        let prices = vec![100.0, 110.0, 120.0, 130.0, 140.0];
        let bars = create_test_bars("TEST", start_time, prices);
        
        let config = BacktestConfig {
            name: "ClosePositionTest".to_string(),
            strategy: None,
            start_time,
            end_time,
            initial_capital: 100000.0,
            commission: 0.0,
            slippage: 0.0,
            symbols: vec!["TEST".to_string()],
            data_frequency: "1d".to_string(),
        };
        
        let time_provider = Box::new(SimulatedTime::new(start_time));
        let mut data_source = Box::new(HistoricalDataSource::new());
        data_source.load_data(bars);
        
        let fill_simulator = Box::new(BacktestFillSimulator::new());
        let execution_handler = Box::new(SimulatedExecution::new(fill_simulator));
        
        let mode = TradingMode::Backtest {
            start_time,
            end_time,
            speed_multiplier: 1.0,
        };
        
        let mut engine = BacktestEngine::new(
            config,
            mode,
            time_provider,
            data_source,
            execution_handler,
        );
        
        // Buy early, never sell (position will be closed at end)
        let strategy = PriceTrackingStrategy::new("ClosePositionTest")
            .with_trades(
                vec![("TEST".to_string(), 1)], // Buy at bar 1 (price 110)
                vec![], // Never sell
            );
        
        engine.add_strategy(Box::new(strategy));
        
        let result = engine.run().await.unwrap();
        
        // Should have 2 trades: the buy and the closing trade
        assert_eq!(result.trades.len(), 2, "Should have buy and closing trade");
        
        let buy_trade = &result.trades[0];
        assert_eq!(buy_trade.side, "buy");
        // Buy at bar 1 executes at the first bar's price (100.0)
        assert!((buy_trade.price - 100.0).abs() < 0.1, "Buy price should be ~100 (bar 1)");
        
        let close_trade = &result.trades[1];
        assert_eq!(close_trade.side, "sell");
        // Position should be closed at the last available price (140)
        assert!(
            (close_trade.price - 140.0).abs() < 0.1,
            "Close price {} should be ~140 (last bar price)", close_trade.price
        );
    }
}