use std::collections::HashMap;
use std::sync::Arc;
use parking_lot::RwLock;
use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};

use crate::{
    TradingMode, MarketDataSource, ExecutionHandler, TimeProvider,
    MarketUpdate, MarketDataType, Order, Fill, Side,
    positions::PositionTracker,
    risk::RiskEngine,
    orderbook::OrderBookManager,
};

use super::{
    BacktestConfig, BacktestState, EventQueue, BacktestEvent, EventType,
    Strategy, Signal, SignalType, BacktestResult, TradeTracker,
};

pub struct BacktestEngine {
    config: BacktestConfig,
    state: Arc<RwLock<BacktestState>>,
    event_queue: Arc<RwLock<EventQueue>>,
    strategies: Arc<RwLock<Vec<Box<dyn Strategy>>>>,
    
    // Core components
    position_tracker: Arc<PositionTracker>,
    risk_engine: Arc<RiskEngine>,
    orderbook_manager: Arc<OrderBookManager>,
    time_provider: Arc<Box<dyn TimeProvider>>,
    pub market_data_source: Arc<RwLock<Box<dyn MarketDataSource>>>,
    execution_handler: Arc<RwLock<Box<dyn ExecutionHandler>>>,
    
    // Metrics
    total_trades: usize,
    profitable_trades: usize,
    total_pnl: f64,
    
    // Price tracking - single source of truth
    // Maps symbol -> (timestamp, price)
    last_prices: HashMap<String, (DateTime<Utc>, f64)>,
    
    // Store latest bar data for each symbol to ensure accurate closing prices
    latest_bars: HashMap<String, crate::Bar>,
    
    // Store all OHLC data processed by the engine
    ohlc_history: HashMap<String, Vec<(DateTime<Utc>, crate::Bar)>>,
    
    // Trade tracking
    trade_tracker: TradeTracker,
}

impl BacktestEngine {
    pub fn new(
        config: BacktestConfig,
        mode: TradingMode,
        time_provider: Box<dyn TimeProvider>,
        market_data_source: Box<dyn MarketDataSource>,
        execution_handler: Box<dyn ExecutionHandler>,
    ) -> Self {
        let state = Arc::new(RwLock::new(
            BacktestState::new(config.initial_capital, config.start_time)
        ));
        
        Self {
            config,
            state,
            event_queue: Arc::new(RwLock::new(EventQueue::new())),
            strategies: Arc::new(RwLock::new(Vec::new())),
            position_tracker: Arc::new(PositionTracker::new()),
            risk_engine: Arc::new(RiskEngine::new()),
            orderbook_manager: Arc::new(OrderBookManager::new()),
            time_provider: Arc::new(time_provider),
            market_data_source: Arc::new(RwLock::new(market_data_source)),
            execution_handler: Arc::new(RwLock::new(execution_handler)),
            total_trades: 0,
            profitable_trades: 0,
            total_pnl: 0.0,
            last_prices: HashMap::new(),
            latest_bars: HashMap::new(),
            ohlc_history: HashMap::new(),
            trade_tracker: TradeTracker::new(),
        }
    }
    
    pub fn add_strategy(&mut self, strategy: Box<dyn Strategy>) {
        self.strategies.write().push(strategy);
    }
    
    pub async fn run(&mut self) -> Result<BacktestResult, String> {
        
        // Initialize start time
        if let Some(simulated_time) = self.time_provider.as_any()
            .downcast_ref::<crate::core::time_providers::SimulatedTime>() 
        {
            simulated_time.advance_to(self.config.start_time);
        }
        
        // Load market data
        self.load_market_data().await?;
        
        let queue_len = self.event_queue.read().len();
        
        if queue_len == 0 {
            return Err("No market data loaded".to_string());
        }
        
        // Main event loop - process events grouped by timestamp
        let mut iteration = 0;
        let mut last_update_time = self.config.start_time;
        
        while !self.event_queue.read().is_empty() {
            iteration += 1;
            
            // Get the next event's timestamp
            let next_event_time = self.event_queue.read()
                .peek_next()
                .map(|e| e.timestamp);
                
            if let Some(event_time) = next_event_time {
                // Advance time to the next event
                self.advance_time(event_time);
                
                // Get all events at this timestamp
                let current_time = self.time_provider.now();
                let events = self.event_queue.write().pop_until(current_time);
                
                // Process all events at this timestamp
                for event in events {
                    self.process_event(event).await?;
                }
                
                // Only update portfolio value if time has actually advanced
                // This ensures we have prices for all symbols at this timestamp
                if current_time > last_update_time {
                    self.update_portfolio_value();
                    last_update_time = current_time;
                }
            } else {
                // No more events
                break;
            }
        }
        
        // Close all open positions at market prices
        self.close_all_positions().await?;
        
        // Generate results
        Ok(self.generate_results())
    }
    
    async fn load_market_data(&mut self) -> Result<(), String> {
        let mut data_source = self.market_data_source.write();
        data_source.seek_to_time(self.config.start_time)?;
        
        // Load all data into event queue
        while let Some(update) = data_source.get_next_update().await {
            if update.timestamp > self.config.end_time {
                break;
            }
            
            let event = BacktestEvent::market_data(update.timestamp, update);
            self.event_queue.write().push(event);
        }
        
        Ok(())
    }
    
    async fn process_event(&mut self, event: BacktestEvent) -> Result<(), String> {
        match event.event_type {
            EventType::MarketData(data) => {
                self.process_market_data(data).await?;
            }
            EventType::OrderSubmitted(order) => {
                self.process_order_submission(order).await?;
            }
            EventType::OrderFilled(_fill) => {
                // Fills are already processed when orders are executed
                // This event is just for recording
                // Note: We now record fills in process_fill with symbol info
            }
            EventType::OrderCancelled(order_id) => {
                self.process_order_cancellation(&order_id)?;
            }
            EventType::TimeUpdate(time) => {
                self.advance_time(time);
            }
        }
        
        Ok(())
    }
    
    async fn process_market_data(&mut self, data: MarketUpdate) -> Result<(), String> {
        // Update price tracking - single source of truth
        let price = match &data.data {
            MarketDataType::Bar(bar) => {
                // Log OHLCV data for each tick
                eprintln!("[TICK] {} @ {} - O:{:.2} H:{:.2} L:{:.2} C:{:.2} V:{:.0}", 
                    data.symbol, 
                    data.timestamp.format("%Y-%m-%d %H:%M:%S"), 
                    bar.open, bar.high, bar.low, bar.close, bar.volume);
                
                // Store the complete bar data for accurate position closing
                self.latest_bars.insert(data.symbol.clone(), bar.clone());
                
                // Store in OHLC history
                self.ohlc_history
                    .entry(data.symbol.clone())
                    .or_insert_with(Vec::new)
                    .push((data.timestamp, bar.clone()));
                
                // Debug: Log OHLC history size periodically
                let history_len = self.ohlc_history.get(&data.symbol).map(|v| v.len()).unwrap_or(0);
                if history_len == 1 || history_len % 100 == 0 {
                    eprintln!("[DEBUG] OHLC history for {}: {} bars stored", data.symbol, history_len);
                }
                
                bar.close
            }
            MarketDataType::Quote(quote) => {
                // Use mid price for quotes
                (quote.bid + quote.ask) / 2.0
            }
            MarketDataType::Trade(trade) => {
                trade.price
            }
        };
        
        // Store price with timestamp - this is our source of truth
        self.last_prices.insert(data.symbol.clone(), (data.timestamp, price));
        
        // Convert to simpler MarketData for strategies
        let market_data = self.convert_to_market_data(&data);
        
        // Send to strategies
        let mut all_signals = Vec::new();
        {
            let mut strategies = self.strategies.write();
            for strategy in strategies.iter_mut() {
                let signals = strategy.on_market_data(&market_data);
                all_signals.extend(signals);
            }
        }
        
        // Process signals
        for signal in all_signals {
            self.process_signal(signal).await?;
        }
        
        // Check pending orders for fills
        self.check_pending_orders(&data).await?;
        
        // Don't update portfolio value here - wait until all events at this timestamp are processed
        
        Ok(())
    }
    
    fn convert_to_market_data(&self, update: &MarketUpdate) -> MarketUpdate {
        // MarketData is a type alias for MarketUpdate
        update.clone()
    }
    
    async fn process_signal(&mut self, signal: Signal) -> Result<(), String> {
        // Only process strong signals
        if signal.strength.abs() < 0.7 {
            return Ok(());
        }
        
        // Convert signal to order
        let order = self.signal_to_order(signal)?;
        
        // Submit order
        self.process_order_submission(order).await
    }
    
    fn signal_to_order(&self, signal: Signal) -> Result<Order, String> {
        let quantity = signal.quantity.unwrap_or_else(|| {
            // Calculate position size based on portfolio
            self.calculate_position_size(&signal.symbol, signal.strength)
        });
        
        let side = match signal.signal_type {
            SignalType::Buy => Side::Buy,
            SignalType::Sell => Side::Sell,
            SignalType::Close => {
                // Determine side based on current position
                let position = self.position_tracker.get_position(&signal.symbol);
                if position.as_ref().map(|p| p.quantity > 0.0).unwrap_or(false) {
                    Side::Sell
                } else {
                    Side::Buy
                }
            }
        };
        
        Ok(crate::Order {
            id: format!("order_{}", uuid::Uuid::new_v4()),
            symbol: signal.symbol,
            side,
            quantity,
            order_type: crate::OrderType::Market,
            time_in_force: crate::TimeInForce::Day,
        })
    }
    
    async fn process_order_submission(&mut self, order: Order) -> Result<(), String> {
        // Risk checks
        // Get current position for the symbol
        let current_position = self.position_tracker
            .get_position(&order.symbol)
            .map(|p| p.quantity);
        
        let risk_check = self.risk_engine.check_order(&order, current_position);
        if !risk_check.passed {
            return Err(format!("Risk check failed: {:?}", risk_check.violations));
        }
        
        // Add to pending orders
        self.state.write().add_pending_order(order.clone());
        
        // For market orders in backtesting, fill immediately
        if matches!(order.order_type, crate::OrderType::Market) {
            self.check_order_fill(&order).await?;
        }
        
        Ok(())
    }
    
    async fn check_pending_orders(&mut self, market_data: &MarketUpdate) -> Result<(), String> {
        let orders_to_check: Vec<Order> = {
            let state = self.state.read();
            state.pending_orders.values()
                .filter(|o| o.symbol == market_data.symbol)
                .cloned()
                .collect()
        };
        
        for order in orders_to_check {
            self.check_order_fill(&order).await?;
        }
        
        Ok(())
    }
    
    async fn check_order_fill(&mut self, order: &Order) -> Result<(), String> {
        let current_time = self.time_provider.now();
        
        // Get current market price
        let (price_time, base_price) = self.last_prices.get(&order.symbol)
            .copied()
            .ok_or_else(|| format!("No price available for symbol: {}", order.symbol))?;
        
        // Apply slippage
        let fill_price = match order.side {
            crate::Side::Buy => base_price * (1.0 + self.config.slippage),
            crate::Side::Sell => base_price * (1.0 - self.config.slippage),
        };
        
        // Get the OHLC data that was used
        let ohlc_info = if let Some(bar) = self.latest_bars.get(&order.symbol) {
            format!("O:{:.2} H:{:.2} L:{:.2} C:{:.2}", bar.open, bar.high, bar.low, bar.close)
        } else {
            "No OHLC data".to_string()
        };
        
        // Log trade execution
        eprintln!("[TRADE] {} {} {} @ {:.2} | OHLC: {} | Time: {}", 
            match order.side {
                crate::Side::Buy => "BUY",
                crate::Side::Sell => "SELL",
            },
            order.quantity,
            order.symbol,
            fill_price,
            ohlc_info,
            current_time.format("%Y-%m-%d %H:%M:%S")
        );
        
        // Create fill
        let fill = crate::Fill {
            timestamp: current_time,
            price: fill_price,
            quantity: order.quantity,
            commission: order.quantity * fill_price * self.config.commission,
        };
        
        // Process the fill
        self.process_fill(&order, fill).await
    }
    
    async fn process_fill(&mut self, order: &crate::Order, fill: crate::Fill) -> Result<(), String> {
        // Remove from pending orders
        self.state.write().remove_pending_order(&order.id);
        
        // Get position before the fill
        let position_before = self.position_tracker
            .get_position(&order.symbol)
            .map(|p| p.quantity)
            .unwrap_or(0.0);
        
        // Update positions
        let update = self.position_tracker.process_fill(
            &order.symbol,
            &fill,
            order.side,
        );
        
        // Calculate P&L if position was reduced/closed
        let pnl = if update.resulting_position.realized_pnl != 0.0 {
            Some(update.resulting_position.realized_pnl)
        } else {
            None
        };
        
        // Get position after this fill
        let position_after = update.resulting_position.quantity;
        
        // Record the fill with position and P&L information
        self.state.write().record_fill(
            order.symbol.clone(), 
            order.side, 
            fill.clone(),
            position_after,
            position_before,
            pnl
        );
        
        // Track trades
        self.trade_tracker.process_fill(&order.symbol, order.side, &fill);
        
        // Update cash
        let cash_change = match order.side {
            crate::Side::Buy => -(fill.quantity * fill.price + fill.commission),
            crate::Side::Sell => fill.quantity * fill.price - fill.commission,
        };
        self.state.write().cash += cash_change;
        
        // Notify strategies
        {
            let mut strategies = self.strategies.write();
            for strategy in strategies.iter_mut() {
                let side_str = match order.side {
                    crate::Side::Buy => "buy",
                    crate::Side::Sell => "sell",
                };
                strategy.on_fill(&order.symbol, fill.quantity, fill.price, side_str);
            }
        }
        
        
        // Update metrics
        self.total_trades += 1;
        if update.resulting_position.realized_pnl > 0.0 {
            self.profitable_trades += 1;
        }
        self.total_pnl = update.resulting_position.realized_pnl;
        
        Ok(())
    }
    
    fn process_order_cancellation(&mut self, order_id: &str) -> Result<(), String> {
        self.state.write().remove_pending_order(order_id);
        Ok(())
    }
    
    fn advance_time(&mut self, time: DateTime<Utc>) {
        if let Some(simulated_time) = self.time_provider.as_any()
            .downcast_ref::<crate::core::time_providers::SimulatedTime>() 
        {
            simulated_time.advance_to(time);
        }
        self.state.write().current_time = time;
        
        // Forward-fill prices: Update timestamps for all symbols to current time
        // This ensures we don't use stale prices when not all symbols have data at every timestamp
        let symbols: Vec<String> = self.last_prices.keys().cloned().collect();
        for symbol in symbols {
            if let Some((old_time, price)) = self.last_prices.get(&symbol).copied() {
                if old_time < time {
                    // Update the timestamp to current time, keeping the same price (forward-fill)
                    self.last_prices.insert(symbol.clone(), (time, price));
                }
            }
        }
    }
    
    fn update_portfolio_value(&mut self) {
        let positions = self.position_tracker.get_all_positions();
        let cash = self.state.read().cash;
        let mut portfolio_value = cash;
        
        for position in &positions {
            // Use last known price for the symbol
            let price = self.last_prices.get(&position.symbol)
                .map(|(_, p)| *p)
                .unwrap_or(position.average_price);
            
            // Calculate market value correctly for long and short positions
            let market_value = if position.quantity > 0.0 {
                // Long position: value = quantity * current_price
                position.quantity * price
            } else {
                // Short position: 
                // We have a liability to buy back shares at current market price
                // This is a negative value that reduces portfolio value
                // Value = quantity * price (quantity is already negative)
                position.quantity * price
            };
            
            portfolio_value += market_value;
        }
        
        self.state.write().update_portfolio_value(portfolio_value);
    }
    
    fn calculate_position_size(&self, symbol: &str, signal_strength: f64) -> f64 {
        let state = self.state.read();
        let portfolio_value = state.portfolio_value;
        let cash = state.cash;
        
        // Use available cash, not total portfolio value for position sizing
        let allocation = 0.2; // 20% of available cash per position
        let position_value = cash.min(portfolio_value * allocation) * signal_strength.abs();
        
        let price = self.last_prices.get(symbol)
            .map(|(_, p)| *p)
            .unwrap_or(100.0);
        (position_value / price).floor()
    }
    
    fn get_next_event_time(&self) -> Option<DateTime<Utc>> {
        // Get the timestamp of the next event in the queue
        self.event_queue.read()
            .peek_next()
            .map(|event| event.timestamp)
    }
    
    async fn close_all_positions(&mut self) -> Result<(), String> {
        // Ensure we have the most recent prices for symbols with positions
        let current_time = self.time_provider.now();
        let positions = self.position_tracker.get_all_positions();
        
        // First, ensure all positions have up-to-date prices
        for position in &positions {
            if position.quantity.abs() > 0.001 {
                // Check if we have bar data for this symbol
                if let Some(latest_bar) = self.latest_bars.get(&position.symbol) {
                    // Use the close price from the latest bar
                    self.last_prices.insert(position.symbol.clone(), (current_time, latest_bar.close));
                } else if let Some((price_time, price)) = self.last_prices.get(&position.symbol).copied() {
                    if price_time < current_time {
                        // Update timestamp to current time for proper fill processing
                        self.last_prices.insert(position.symbol.clone(), (current_time, price));
                    }
                }
            }
        }
        
        for position in positions {
            if position.quantity.abs() > 0.001 {
                // Create market order to close position
                let order = crate::Order {
                    id: format!("close_{}", uuid::Uuid::new_v4()),
                    symbol: position.symbol.clone(),
                    side: if position.quantity > 0.0 { Side::Sell } else { Side::Buy },
                    quantity: position.quantity.abs(),
                    order_type: crate::OrderType::Market,
                    time_in_force: crate::TimeInForce::Day,
                };
                
                // Process the closing order
                self.check_order_fill(&order).await?;
            }
        }
        
        Ok(())
    }
    
    fn generate_results(&self) -> BacktestResult {
        let state = self.state.read();
        let start_time = self.config.start_time;
        
        // Get final positions
        let final_positions = self.position_tracker.get_all_positions()
            .into_iter()
            .map(|p| (p.symbol.clone(), p))
            .collect();
        
        // Get completed trades from tracker
        let completed_trades = self.trade_tracker.get_completed_trades();
        
        // Convert last_prices to simple HashMap for results
        let simple_last_prices: HashMap<String, f64> = self.last_prices
            .iter()
            .map(|(symbol, (_, price))| (symbol.clone(), *price))
            .collect();
        
        // Debug: Log OHLC history size before generating results
        eprintln!("[DEBUG] Generating results with OHLC history:");
        for (symbol, bars) in &self.ohlc_history {
            eprintln!("  {}: {} bars", symbol, bars.len());
        }
        
        // Use simple results builder with proper trade data
        BacktestResult::from_engine_data_with_trades(
            self.config.clone(),
            state.equity_curve.clone(),
            state.completed_trades.clone(),
            completed_trades,
            final_positions,
            start_time,
            &simple_last_prices,
            &self.ohlc_history,
        )
    }
}

// Add uuid dependency
use uuid::Uuid;