use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::sync::Arc;
use parking_lot::RwLock;
use tokio::sync::mpsc;
use crate::{
    MarketDataSource, ExecutionHandler, TimeProvider, FillSimulator,
    MarketUpdate, Order, OrderStatus, Fill,
};
use crate::events::EventBus;
use crate::domain::{Event, EventType};
use super::TradingEngine;

/// Paper trading engine - simulates live trading without real money
pub struct PaperEngine {
    starting_capital: f64,
    market_data: Box<dyn MarketDataSource>,
    execution: PaperExecutor,
    is_running: Arc<RwLock<bool>>,
    shutdown_tx: Option<mpsc::Sender<()>>,
}

impl PaperEngine {
    pub fn new(
        starting_capital: f64,
        market_data: Box<dyn MarketDataSource>,
    ) -> Self {
        Self {
            starting_capital,
            market_data,
            execution: PaperExecutor::new(starting_capital),
            is_running: Arc::new(RwLock::new(false)),
            shutdown_tx: None,
        }
    }
}

#[async_trait]
impl TradingEngine for PaperEngine {
    async fn start(&mut self, event_bus: &mut EventBus) -> Result<(), String> {
        *self.is_running.write() = true;
        
        let (shutdown_tx, mut shutdown_rx) = mpsc::channel(1);
        self.shutdown_tx = Some(shutdown_tx);
        
        // Emit start event
        event_bus.publish(Event {
            event_type: EventType::SystemStart,
            timestamp: Utc::now(),
            data: serde_json::json!({
                "mode": "paper",
                "starting_capital": self.starting_capital,
            }),
        }).await;
        
        // Main paper trading loop
        loop {
            tokio::select! {
                _ = shutdown_rx.recv() => {
                    break;
                }
                update = self.market_data.get_next_update() => {
                    if let Some(market_update) = update {
                        // Publish market data event
                        event_bus.publish(Event {
                            event_type: EventType::MarketData(market_update.clone()),
                            timestamp: Utc::now(),
                            data: serde_json::Value::Null,
                        }).await;
                        
                        // Process pending orders
                        self.execution.process_orders(&market_update, event_bus).await?;
                    }
                }
                _ = tokio::time::sleep(std::time::Duration::from_millis(100)) => {
                    // Check if still running
                    if !*self.is_running.read() {
                        break;
                    }
                }
            }
        }
        
        // Emit stop event
        event_bus.publish(Event {
            event_type: EventType::SystemStop,
            timestamp: Utc::now(),
            data: serde_json::json!({
                "reason": "paper_trading_stopped",
            }),
        }).await;
        
        Ok(())
    }
    
    async fn stop(&mut self) -> Result<(), String> {
        *self.is_running.write() = false;
        if let Some(tx) = self.shutdown_tx.take() {
            let _ = tx.send(()).await;
        }
        Ok(())
    }
    
    fn get_execution_handler(&self) -> Arc<dyn ExecutionHandler> {
        Arc::new(self.execution.clone())
    }
    
    fn get_time_provider(&self) -> Arc<dyn TimeProvider> {
        Arc::new(RealTimeProvider)
    }
}

/// Paper trading order executor
#[derive(Clone)]
struct PaperExecutor {
    cash: Arc<RwLock<f64>>,
    pending_orders: Arc<RwLock<Vec<Order>>>,
    order_history: Arc<RwLock<Vec<(Order, OrderStatus)>>>,
    fill_simulator: PaperFillSimulator,
}

impl PaperExecutor {
    fn new(starting_capital: f64) -> Self {
        Self {
            cash: Arc::new(RwLock::new(starting_capital)),
            pending_orders: Arc::new(RwLock::new(Vec::new())),
            order_history: Arc::new(RwLock::new(Vec::new())),
            fill_simulator: PaperFillSimulator::new(),
        }
    }
    
    async fn process_orders(
        &self,
        market_update: &MarketUpdate,
        event_bus: &mut EventBus,
    ) -> Result<(), String> {
        let mut orders = self.pending_orders.write();
        let mut filled_indices = Vec::new();
        
        for (idx, order) in orders.iter().enumerate() {
            if order.symbol != market_update.symbol {
                continue;
            }
            
            // Try to fill the order
            if let Some(fill) = self.fill_simulator.try_fill(order, market_update) {
                // Check if we have enough cash for buy orders
                if order.side == crate::Side::Buy {
                    let required_cash = fill.price * fill.quantity + fill.commission;
                    let mut cash = self.cash.write();
                    if *cash >= required_cash {
                        *cash -= required_cash;
                    } else {
                        continue; // Skip this order
                    }
                } else {
                    // For sell orders, add cash
                    let proceeds = fill.price * fill.quantity - fill.commission;
                    *self.cash.write() += proceeds;
                }
                
                // Emit fill event
                event_bus.publish(Event {
                    event_type: EventType::OrderFill {
                        order_id: order.id.clone(),
                        fill: fill.clone(),
                    },
                    timestamp: Utc::now(),
                    data: serde_json::json!({
                        "cash_after": *self.cash.read(),
                    }),
                }).await;
                
                filled_indices.push(idx);
            }
        }
        
        // Remove filled orders
        for idx in filled_indices.into_iter().rev() {
            let order = orders.remove(idx);
            self.order_history.write().push((order, OrderStatus::Filled));
        }
        
        Ok(())
    }
}

#[async_trait]
impl ExecutionHandler for PaperExecutor {
    async fn submit_order(&self, order: &Order) -> Result<String, String> {
        // Basic validation
        if order.quantity <= 0.0 {
            return Err("Invalid order quantity".to_string());
        }
        
        let order_id = order.id.clone();
        self.pending_orders.write().push(order.clone());
        Ok(order_id)
    }
    
    async fn cancel_order(&self, order_id: &str) -> Result<(), String> {
        let mut orders = self.pending_orders.write();
        if let Some(pos) = orders.iter().position(|o| o.id == order_id) {
            let order = orders.remove(pos);
            self.order_history.write().push((order, OrderStatus::Cancelled));
            Ok(())
        } else {
            Err("Order not found".to_string())
        }
    }
    
    async fn get_order_status(&self, order_id: &str) -> Result<OrderStatus, String> {
        // Check pending orders
        if self.pending_orders.read().iter().any(|o| o.id == order_id) {
            return Ok(OrderStatus::Pending);
        }
        
        // Check history
        if let Some((_, status)) = self.order_history.read()
            .iter()
            .find(|(o, _)| o.id == order_id) {
            return Ok(status.clone());
        }
        
        Err("Order not found".to_string())
    }
}

/// Real-time provider for paper trading
struct RealTimeProvider;

impl TimeProvider for RealTimeProvider {
    fn now(&self) -> DateTime<Utc> {
        Utc::now()
    }
    
    fn sleep_until(&self, target: DateTime<Utc>) -> Result<(), String> {
        let now = Utc::now();
        if target > now {
            let duration = target.signed_duration_since(now);
            std::thread::sleep(duration.to_std().unwrap_or_default());
        }
        Ok(())
    }
}

/// Paper trading fill simulator
#[derive(Clone)]
struct PaperFillSimulator {
    commission_rate: f64,
    slippage_bps: f64,
}

impl PaperFillSimulator {
    fn new() -> Self {
        Self {
            commission_rate: 0.001, // 0.1%
            slippage_bps: 3.0, // 3 basis points (less than backtest)
        }
    }
    
    fn try_fill(&self, order: &Order, market_update: &MarketUpdate) -> Option<Fill> {
        match &market_update.data {
            crate::MarketDataType::Quote(quote) => {
                self.simulate_fill(order, quote.bid, quote.ask - quote.bid)
            }
            crate::MarketDataType::Trade(trade) => {
                self.simulate_fill(order, trade.price, 0.0001 * trade.price)
            }
            crate::MarketDataType::Bar(bar) => {
                self.simulate_fill(order, bar.close, 0.0002 * bar.close)
            }
        }
    }
}

impl FillSimulator for PaperFillSimulator {
    fn simulate_fill(&self, order: &Order, market_price: f64, spread: f64) -> Option<Fill> {
        let fill_price = match order.order_type {
            crate::OrderType::Market => {
                let slippage = market_price * self.slippage_bps / 10000.0;
                match order.side {
                    crate::Side::Buy => market_price + spread/2.0 + slippage,
                    crate::Side::Sell => market_price - spread/2.0 - slippage,
                }
            }
            crate::OrderType::Limit { price } => {
                match order.side {
                    crate::Side::Buy if price >= market_price + spread/2.0 => price,
                    crate::Side::Sell if price <= market_price - spread/2.0 => price,
                    _ => return None,
                }
            }
            _ => return None,
        };
        
        let commission = fill_price * order.quantity * self.commission_rate;
        
        Some(Fill {
            timestamp: Utc::now(),
            price: fill_price,
            quantity: order.quantity,
            commission,
        })
    }
}