mod indicators;
mod risk;
mod backtest;

pub use indicators::{TechnicalIndicators, IncrementalSMA, IncrementalEMA, IncrementalRSI};
pub use risk::{RiskAnalyzer, OrderbookAnalyzer};
pub use backtest::BacktestEngine;

use napi_derive::napi;
use napi::{bindgen_prelude::*, JsObject};
use crate::{
    TradingCore, TradingMode, Order, OrderType, TimeInForce, Side,
    MarketUpdate, Quote, Trade,
    MarketMicrostructure,
    core::{create_market_data_source, create_execution_handler, create_time_provider},
};
use crate::risk::RiskLimits;
use std::sync::Arc;
use parking_lot::Mutex;
use chrono::{DateTime, Utc};

#[napi]
pub struct TradingEngine {
    core: Arc<Mutex<TradingCore>>,
}

#[napi]
impl TradingEngine {
    #[napi(constructor)]
    pub fn new(mode: String, config: JsObject) -> Result<Self> {
        let mode = parse_mode(&mode, config)?;
        
        let market_data_source = create_market_data_source(&mode);
        let execution_handler = create_execution_handler(&mode);
        let time_provider = create_time_provider(&mode);
        
        let core = TradingCore::new(mode, market_data_source, execution_handler, time_provider);
        
        Ok(Self {
            core: Arc::new(Mutex::new(core)),
        })
    }

    #[napi]
    pub fn get_mode(&self) -> String {
        let core = self.core.lock();
        match core.get_mode() {
            TradingMode::Backtest { .. } => "backtest".to_string(),
            TradingMode::Paper { .. } => "paper".to_string(),
            TradingMode::Live { .. } => "live".to_string(),
        }
    }

    #[napi]
    pub fn get_current_time(&self) -> i64 {
        let core = self.core.lock();
        core.get_time().timestamp_millis()
    }

    #[napi]
    pub fn submit_order(&self, order_js: JsObject) -> Result<String> {
        let order = parse_order(order_js)?;
        
        // For now, return a mock result - in real implementation would queue the order
        let result = crate::ExecutionResult {
            order_id: order.id.clone(),
            status: crate::OrderStatus::Accepted,
            fills: vec![],
        };
        
        Ok(serde_json::to_string(&result).unwrap())
    }

    #[napi]
    pub fn check_risk(&self, order_js: JsObject) -> Result<String> {
        let order = parse_order(order_js)?;
        let core = self.core.lock();
        
        // Get current position for the symbol
        let position = core.position_tracker.get_position(&order.symbol);
        let current_quantity = position.map(|p| p.quantity);
        
        let result = core.risk_engine.check_order(&order, current_quantity);
        Ok(serde_json::to_string(&result).unwrap())
    }

    #[napi]
    pub fn update_quote(&self, symbol: String, bid: f64, ask: f64, bid_size: f64, ask_size: f64) -> Result<()> {
        let quote = Quote { bid, ask, bid_size, ask_size };
        let core = self.core.lock();
        let timestamp = core.get_time();
        
        core.orderbooks.update_quote(&symbol, quote, timestamp);
        
        // Update unrealized P&L
        let mid_price = (bid + ask) / 2.0;
        core.position_tracker.update_unrealized_pnl(&symbol, mid_price);
        
        Ok(())
    }

    #[napi]
    pub fn update_trade(&self, symbol: String, price: f64, size: f64, side: String) -> Result<()> {
        let side = match side.as_str() {
            "buy" | "Buy" => Side::Buy,
            "sell" | "Sell" => Side::Sell,
            _ => return Err(Error::from_reason("Invalid side")),
        };
        
        let trade = Trade { price, size, side };
        let core = self.core.lock();
        let timestamp = core.get_time();
        
        core.orderbooks.update_trade(&symbol, trade, timestamp);
        Ok(())
    }

    #[napi]
    pub fn get_orderbook_snapshot(&self, symbol: String, depth: u32) -> Result<String> {
        let core = self.core.lock();
        let snapshot = core.orderbooks.get_snapshot(&symbol, depth as usize)
            .ok_or_else(|| Error::from_reason("Symbol not found"))?;
        
        Ok(serde_json::to_string(&snapshot).unwrap())
    }

    #[napi]
    pub fn get_best_bid_ask(&self, symbol: String) -> Result<Vec<f64>> {
        let core = self.core.lock();
        let (bid, ask) = core.orderbooks.get_best_bid_ask(&symbol)
            .ok_or_else(|| Error::from_reason("Symbol not found"))?;
        
        Ok(vec![bid, ask])
    }

    #[napi]
    pub fn get_position(&self, symbol: String) -> Result<Option<String>> {
        let core = self.core.lock();
        let position = core.position_tracker.get_position(&symbol);
        
        Ok(position.map(|p| serde_json::to_string(&p).unwrap()))
    }

    #[napi]
    pub fn get_all_positions(&self) -> Result<String> {
        let core = self.core.lock();
        let positions = core.position_tracker.get_all_positions();
        
        Ok(serde_json::to_string(&positions).unwrap())
    }

    #[napi]
    pub fn get_open_positions(&self) -> Result<String> {
        let core = self.core.lock();
        let positions = core.position_tracker.get_open_positions();
        
        Ok(serde_json::to_string(&positions).unwrap())
    }

    #[napi]
    pub fn get_total_pnl(&self) -> Result<Vec<f64>> {
        let core = self.core.lock();
        let (realized, unrealized) = core.position_tracker.get_total_pnl();
        
        Ok(vec![realized, unrealized])
    }

    #[napi]
    pub fn process_fill(&self, symbol: String, price: f64, quantity: f64, side: String, commission: f64) -> Result<String> {
        self.process_fill_with_metadata(symbol, price, quantity, side, commission, None, None)
    }
    
    #[napi]
    pub fn process_fill_with_metadata(
        &self, 
        symbol: String, 
        price: f64, 
        quantity: f64, 
        side: String, 
        commission: f64,
        order_id: Option<String>,
        strategy_id: Option<String>
    ) -> Result<String> {
        let side = match side.as_str() {
            "buy" | "Buy" => Side::Buy,
            "sell" | "Sell" => Side::Sell,
            _ => return Err(Error::from_reason("Invalid side")),
        };
        
        let core = self.core.lock();
        let timestamp = core.get_time();
        
        let fill = crate::Fill {
            timestamp,
            price,
            quantity,
            commission,
        };
        
        let update = core.position_tracker.process_fill_with_tracking(&symbol, &fill, side, order_id, strategy_id);
        
        // Update risk engine with new position
        core.risk_engine.update_position(&symbol, update.resulting_position.quantity);
        
        // Update daily P&L
        if update.resulting_position.realized_pnl != 0.0 {
            core.risk_engine.update_daily_pnl(update.resulting_position.realized_pnl);
        }
        
        Ok(serde_json::to_string(&update).unwrap())
    }

    #[napi]
    pub fn update_risk_limits(&self, limits_js: JsObject) -> Result<()> {
        let limits = parse_risk_limits(limits_js)?;
        let core = self.core.lock();
        core.risk_engine.update_limits(limits);
        Ok(())
    }

    #[napi]
    pub fn reset_daily_metrics(&self) -> Result<()> {
        let core = self.core.lock();
        core.risk_engine.reset_daily_metrics();
        Ok(())
    }

    #[napi]
    pub fn get_risk_metrics(&self) -> Result<String> {
        let core = self.core.lock();
        let metrics = core.risk_engine.get_risk_metrics();
        Ok(serde_json::to_string(&metrics).unwrap())
    }

    // Backtest-specific methods
    #[napi]
    pub fn advance_time(&self, to_timestamp: i64) -> Result<()> {
        let core = self.core.lock();
        if let TradingMode::Backtest { .. } = core.get_mode() {
            // Downcast time provider to SimulatedTime and advance it
            if let Some(simulated_time) = core.time_provider.as_any().downcast_ref::<crate::core::time_providers::SimulatedTime>() {
                let new_time = DateTime::<Utc>::from_timestamp_millis(to_timestamp)
                    .ok_or_else(|| Error::from_reason("Invalid timestamp"))?;
                simulated_time.advance_to(new_time);
                Ok(())
            } else {
                Err(Error::from_reason("Failed to access simulated time provider"))
            }
        } else {
            Err(Error::from_reason("Can only advance time in backtest mode"))
        }
    }
    
    #[napi]
    pub fn set_microstructure(&self, symbol: String, microstructure_js: JsObject) -> Result<()> {
        let microstructure = parse_microstructure(microstructure_js)?;
        
        let _core = self.core.lock();
        // Store microstructure for use in fill simulation
        // In real implementation, would pass to execution handler
        Ok(())
    }

    #[napi]
    pub fn load_historical_data(&self, data_json: String) -> Result<()> {
        let data: Vec<MarketUpdate> = serde_json::from_str(&data_json)
            .map_err(|e| Error::from_reason(format!("Failed to parse data: {}", e)))?;
        
        let core = self.core.lock();
        
        // Downcast to HistoricalDataSource if in backtest mode
        if let TradingMode::Backtest { .. } = core.get_mode() {
            let mut data_source = core.market_data_source.write();
            if let Some(historical_source) = data_source.as_any_mut().downcast_mut::<crate::core::market_data_sources::HistoricalDataSource>() {
                historical_source.load_data(data);
            }
        }
        
        Ok(())
    }
    
    #[napi]
    pub fn generate_mock_data(&self, symbol: String, start_time: i64, end_time: i64, seed: Option<u32>) -> Result<()> {
        let core = self.core.lock();
        
        // Only available in backtest mode
        if let TradingMode::Backtest { .. } = core.get_mode() {
            let mut data_source = core.market_data_source.write();
            if let Some(historical_source) = data_source.as_any_mut().downcast_mut::<crate::core::market_data_sources::HistoricalDataSource>() {
                let start_dt = DateTime::<Utc>::from_timestamp_millis(start_time)
                    .ok_or_else(|| Error::from_reason("Invalid start time"))?;
                let end_dt = DateTime::<Utc>::from_timestamp_millis(end_time)
                    .ok_or_else(|| Error::from_reason("Invalid end time"))?;
                
                historical_source.generate_mock_data(symbol, start_dt, end_dt, seed.map(|s| s as u64));
            } else {
                return Err(Error::from_reason("Failed to access historical data source"));
            }
        } else {
            return Err(Error::from_reason("Mock data generation only available in backtest mode"));
        }
        
        Ok(())
    }
    
    #[napi]
    pub fn get_trade_history(&self) -> Result<String> {
        let core = self.core.lock();
        let trades = core.position_tracker.get_trade_history();
        Ok(serde_json::to_string(&trades).unwrap())
    }
    
    #[napi]
    pub fn get_closed_trades(&self) -> Result<String> {
        let core = self.core.lock();
        let trades = core.position_tracker.get_closed_trades();
        Ok(serde_json::to_string(&trades).unwrap())
    }
    
    #[napi]
    pub fn get_open_trades(&self) -> Result<String> {
        let core = self.core.lock();
        let trades = core.position_tracker.get_open_trades();
        Ok(serde_json::to_string(&trades).unwrap())
    }
    
    #[napi]
    pub fn get_trade_count(&self) -> Result<u32> {
        let core = self.core.lock();
        Ok(core.position_tracker.get_trade_count() as u32)
    }
    
    #[napi]
    pub fn get_closed_trade_count(&self) -> Result<u32> {
        let core = self.core.lock();
        Ok(core.position_tracker.get_closed_trade_count() as u32)
    }
}

// Helper functions to parse JavaScript objects
fn parse_mode(mode_str: &str, config: JsObject) -> Result<TradingMode> {
    match mode_str {
        "backtest" => {
            let start_time: i64 = config.get_named_property("startTime")?;
            let end_time: i64 = config.get_named_property("endTime")?;
            let speed_multiplier: f64 = config.get_named_property("speedMultiplier")
                .unwrap_or(1.0);
            
            Ok(TradingMode::Backtest {
                start_time: DateTime::<Utc>::from_timestamp_millis(start_time)
                    .ok_or_else(|| Error::from_reason("Invalid start time"))?,
                end_time: DateTime::<Utc>::from_timestamp_millis(end_time)
                    .ok_or_else(|| Error::from_reason("Invalid end time"))?,
                speed_multiplier,
            })
        }
        "paper" => {
            let starting_capital: f64 = config.get_named_property("startingCapital")?;
            Ok(TradingMode::Paper { starting_capital })
        }
        "live" => {
            let broker: String = config.get_named_property("broker")?;
            let account_id: String = config.get_named_property("accountId")?;
            Ok(TradingMode::Live { broker, account_id })
        }
        _ => Err(Error::from_reason("Invalid mode")),
    }
}

fn parse_order(order_js: JsObject) -> Result<Order> {
    let id: String = order_js.get_named_property("id")?;
    let symbol: String = order_js.get_named_property("symbol")?;
    let side_str: String = order_js.get_named_property("side")?;
    let side = match side_str.as_str() {
        "buy" | "Buy" => Side::Buy,
        "sell" | "Sell" => Side::Sell,
        _ => return Err(Error::from_reason("Invalid side")),
    };
    let quantity: f64 = order_js.get_named_property("quantity")?;
    
    let order_type_str: String = order_js.get_named_property("orderType")?;
    let order_type = match order_type_str.as_str() {
        "market" => OrderType::Market,
        "limit" => {
            let price: f64 = order_js.get_named_property("limitPrice")?;
            OrderType::Limit { price }
        }
        _ => return Err(Error::from_reason("Invalid order type")),
    };
    
    let time_in_force_str: String = order_js.get_named_property("timeInForce")
        .unwrap_or_else(|_| "DAY".to_string());
    let time_in_force = match time_in_force_str.as_str() {
        "DAY" => TimeInForce::Day,
        "GTC" => TimeInForce::GTC,
        "IOC" => TimeInForce::IOC,
        "FOK" => TimeInForce::FOK,
        _ => TimeInForce::Day,
    };
    
    Ok(Order {
        id,
        symbol,
        side,
        quantity,
        order_type,
        time_in_force,
    })
}

fn parse_risk_limits(limits_js: JsObject) -> Result<RiskLimits> {
    Ok(RiskLimits {
        max_position_size: limits_js.get_named_property("maxPositionSize")?,
        max_order_size: limits_js.get_named_property("maxOrderSize")?,
        max_daily_loss: limits_js.get_named_property("maxDailyLoss")?,
        max_gross_exposure: limits_js.get_named_property("maxGrossExposure")?,
        max_symbol_exposure: limits_js.get_named_property("maxSymbolExposure")?,
    })
}

fn parse_microstructure(microstructure_js: JsObject) -> Result<MarketMicrostructure> {
    let intraday_volume_profile: Vec<f64> = microstructure_js.get_named_property("intradayVolumeProfile")
        .unwrap_or_else(|_| vec![1.0/24.0; 24]);
    
    Ok(MarketMicrostructure {
        symbol: microstructure_js.get_named_property("symbol")?,
        avg_spread_bps: microstructure_js.get_named_property("avgSpreadBps")?,
        daily_volume: microstructure_js.get_named_property("dailyVolume")?,
        avg_trade_size: microstructure_js.get_named_property("avgTradeSize")?,
        volatility: microstructure_js.get_named_property("volatility")?,
        tick_size: microstructure_js.get_named_property("tickSize")?,
        lot_size: microstructure_js.get_named_property("lotSize")?,
        intraday_volume_profile,
    })
}