use napi::bindgen_prelude::*;
use napi_derive::napi;
use std::sync::Arc;
use parking_lot::Mutex;
use crate::backtest::{
    BacktestEngine as RustBacktestEngine, 
    BacktestConfig,
    Strategy, Signal,
};
use crate::{TradingMode, MarketUpdate};
use chrono::{DateTime, Utc};

#[napi]
pub struct BacktestEngine {
    inner: Arc<Mutex<Option<RustBacktestEngine>>>,
}

#[napi]
impl BacktestEngine {
    #[napi(constructor)]
    pub fn new(config: napi::JsObject, env: Env) -> Result<Self> {
        let config = parse_backtest_config(config)?;
        
        // Create mode
        let mode = TradingMode::Backtest {
            start_time: config.start_time,
            end_time: config.end_time,
            speed_multiplier: 0.0, // Max speed
        };
        
        // Create components
        let time_provider = crate::core::create_time_provider(&mode);
        let market_data_source = crate::core::create_market_data_source(&mode);
        let execution_handler = crate::core::create_execution_handler(&mode);
        
        let engine = RustBacktestEngine::new(
            config,
            mode,
            time_provider,
            market_data_source,
            execution_handler,
        );
        
        Ok(Self {
            inner: Arc::new(Mutex::new(Some(engine))),
        })
    }
    
    #[napi]
    pub fn add_typescript_strategy(
        &mut self, 
        name: String, 
        id: String,
        parameters: napi::JsObject,
        _callback: napi::JsFunction,
    ) -> Result<()> {
        eprintln!("Adding strategy: {}", name);
        
        // For now, we'll add a native Rust SMA strategy
        // In the future, we'll implement proper TypeScript callback support
        let fast_period: usize = parameters.get_named_property::<f64>("fastPeriod")
            .unwrap_or(5.0) as usize;
        let slow_period: usize = parameters.get_named_property::<f64>("slowPeriod")
            .unwrap_or(15.0) as usize;
        
        if let Some(engine) = self.inner.lock().as_mut() {
            engine.add_strategy(Box::new(SimpleSMAStrategy::new(
                name.clone(),
                id,
                fast_period,
                slow_period,
            )));
            eprintln!("Strategy '{}' added with fast={}, slow={}", name, fast_period, slow_period);
        }
        
        Ok(())
    }
    
    #[napi]
    pub fn add_native_strategy(
        &mut self,
        strategy_type: String,
        name: String,
        id: String,
        parameters: napi::JsObject,
    ) -> Result<()> {
        eprintln!("Adding native Rust strategy: {} ({})", name, strategy_type);
        
        if let Some(engine) = self.inner.lock().as_mut() {
            match strategy_type.as_str() {
                "sma_crossover" => {
                    let fast_period: usize = parameters.get_named_property::<f64>("fastPeriod")
                        .unwrap_or(5.0) as usize;
                    let slow_period: usize = parameters.get_named_property::<f64>("slowPeriod")
                        .unwrap_or(15.0) as usize;
                    
                    engine.add_strategy(Box::new(SimpleSMAStrategy::new(
                        name.clone(),
                        id,
                        fast_period,
                        slow_period,
                    )));
                }
                "mean_reversion" => {
                    let lookback_period: usize = parameters.get_named_property::<f64>("lookbackPeriod")
                        .unwrap_or(20.0) as usize;
                    let entry_threshold: f64 = parameters.get_named_property::<f64>("entryThreshold")
                        .unwrap_or(2.0);
                    let position_size: f64 = parameters.get_named_property::<f64>("positionSize")
                        .unwrap_or(100.0);
                    
                    engine.add_strategy(Box::new(crate::strategies::MeanReversionFixedStrategy::new(
                        name.clone(),
                        id,
                        lookback_period,
                        entry_threshold,
                        position_size,
                    )));
                }
                "momentum" => {
                    let lookback_period: usize = parameters.get_named_property::<f64>("lookbackPeriod")
                        .unwrap_or(14.0) as usize;
                    let momentum_threshold: f64 = parameters.get_named_property::<f64>("momentumThreshold")
                        .unwrap_or(5.0);
                    let position_size: f64 = parameters.get_named_property::<f64>("positionSize")
                        .unwrap_or(100.0);
                    
                    engine.add_strategy(Box::new(crate::strategies::MomentumStrategy::new(
                        name.clone(),
                        id,
                        lookback_period,
                        momentum_threshold,
                        position_size,
                    )));
                }
                "pairs_trading" => {
                    let pair_a: String = parameters.get_named_property::<String>("pairA")?;
                    let pair_b: String = parameters.get_named_property::<String>("pairB")?;
                    let lookback_period: usize = parameters.get_named_property::<f64>("lookbackPeriod")
                        .unwrap_or(20.0) as usize;
                    let entry_threshold: f64 = parameters.get_named_property::<f64>("entryThreshold")
                        .unwrap_or(2.0);
                    let position_size: f64 = parameters.get_named_property::<f64>("positionSize")
                        .unwrap_or(100.0);
                    
                    engine.add_strategy(Box::new(crate::strategies::PairsTradingStrategy::new(
                        name.clone(),
                        id,
                        pair_a,
                        pair_b,
                        lookback_period,
                        entry_threshold,
                        position_size,
                    )));
                }
                _ => {
                    return Err(Error::from_reason(format!("Unknown strategy type: {}", strategy_type)));
                }
            }
            eprintln!("Native strategy '{}' added successfully", name);
        }
        
        Ok(())
    }
    
    #[napi]
    pub fn run(&mut self) -> Result<String> {
        eprintln!("=== BACKTEST RUN START ===");
        let mut engine = self.inner.lock().take()
            .ok_or_else(|| Error::from_reason("Engine already consumed"))?;
        
        // Config and strategies are private, skip detailed logging
        
        // Run the backtest synchronously for now
        let runtime = tokio::runtime::Runtime::new()
            .map_err(|e| Error::from_reason(e.to_string()))?;
        
        let result = runtime.block_on(engine.run())
            .map_err(|e| {
                eprintln!("ERROR: Backtest engine failed: {}", e);
                Error::from_reason(e)
            })?;
        
        eprintln!("=== BACKTEST RUN COMPLETE ===");
        eprintln!("Total trades: {}", result.trades.len());
        eprintln!("Equity points: {}", result.equity.len());
        eprintln!("OHLC data symbols: {:?}", result.ohlc_data.keys().collect::<Vec<_>>());
        for (symbol, data) in &result.ohlc_data {
            eprintln!("  {}: {} bars", symbol, data.len());
        }
        
        // Return result as JSON
        serde_json::to_string(&result)
            .map_err(|e| Error::from_reason(e.to_string()))
    }
    
    #[napi]
    pub fn load_market_data(&self, data: Vec<napi::JsObject>) -> Result<()> {
        eprintln!("load_market_data called with {} items", data.len());
        
        // Convert JS objects to MarketData
        let market_data: Vec<MarketUpdate> = data.into_iter()
            .filter_map(|obj| parse_market_data(obj).ok())
            .collect();
        
        eprintln!("Parsed {} valid market data items", market_data.len());
        
        // Load data into the historical data source
        if let Some(engine) = self.inner.lock().as_ref() {
            // Access the market data source through the engine
            let mut data_source = engine.market_data_source.write();
            if let Some(historical_source) = data_source.as_any_mut()
                .downcast_mut::<crate::core::market_data_sources::HistoricalDataSource>() {
                eprintln!("Loading data into HistoricalDataSource");
                historical_source.load_data(market_data);
                eprintln!("Data loaded successfully");
            } else {
                eprintln!("ERROR: Could not downcast to HistoricalDataSource");
            }
        } else {
            eprintln!("ERROR: Engine not found");
        }
        
        Ok(())
    }
}


fn parse_backtest_config(obj: napi::JsObject) -> Result<BacktestConfig> {
    let name: String = obj.get_named_property("name")?;
    let symbols: Vec<String> = obj.get_named_property("symbols")?;
    let start_date: String = obj.get_named_property("startDate")?;
    let end_date: String = obj.get_named_property("endDate")?;
    let initial_capital: f64 = obj.get_named_property("initialCapital")?;
    let commission: f64 = obj.get_named_property("commission")?;
    let slippage: f64 = obj.get_named_property("slippage")?;
    let data_frequency: String = obj.get_named_property("dataFrequency")?;
    let strategy: Option<String> = obj.get_named_property("strategy").ok();
    
    Ok(BacktestConfig {
        name,
        strategy,
        symbols,
        start_time: DateTime::parse_from_rfc3339(&start_date)
            .map_err(|e| Error::from_reason(e.to_string()))?
            .with_timezone(&Utc),
        end_time: DateTime::parse_from_rfc3339(&end_date)
            .map_err(|e| Error::from_reason(e.to_string()))?
            .with_timezone(&Utc),
        initial_capital,
        commission,
        slippage,
        data_frequency,
    })
}

fn parse_market_data(obj: napi::JsObject) -> Result<crate::MarketUpdate> {
    let symbol: String = obj.get_named_property("symbol")?;
    let timestamp: i64 = obj.get_named_property("timestamp")?;
    let data_type: String = obj.get_named_property("type")?;
    
    let data = if data_type == "bar" {
        crate::MarketDataType::Bar(crate::Bar {
            open: obj.get_named_property("open")?,
            high: obj.get_named_property("high")?,
            low: obj.get_named_property("low")?,
            close: obj.get_named_property("close")?,
            volume: obj.get_named_property("volume")?,
            vwap: obj.get_named_property("vwap").ok(),
        })
    } else {
        eprintln!("Unsupported market data type: {}", data_type);
        return Err(Error::from_reason("Unsupported market data type"));
    };
    
    // First few items
    static mut COUNT: usize = 0;
    unsafe {
        if COUNT < 3 {
            eprintln!("Parsed market data: symbol={}, timestamp={}, close={}", 
                symbol, timestamp, 
                if let crate::MarketDataType::Bar(ref bar) = data { bar.close } else { 0.0 });
            COUNT += 1;
        }
    }
    
    Ok(crate::MarketUpdate {
        symbol,
        timestamp: DateTime::<Utc>::from_timestamp(timestamp / 1000, 0)
            .ok_or_else(|| Error::from_reason("Invalid timestamp"))?,
        data,
    })
}

// Simple SMA Strategy for testing
struct SimpleSMAStrategy {
    name: String,
    id: String,
    fast_period: usize,
    slow_period: usize,
    price_history: std::collections::HashMap<String, Vec<f64>>,
    positions: std::collections::HashMap<String, f64>,
}

impl SimpleSMAStrategy {
    fn new(name: String, id: String, fast_period: usize, slow_period: usize) -> Self {
        eprintln!("Creating SimpleSMAStrategy: name={}, fast={}, slow={}", name, fast_period, slow_period);
        Self {
            name,
            id,
            fast_period,
            slow_period,
            price_history: std::collections::HashMap::new(),
            positions: std::collections::HashMap::new(),
        }
    }
}

impl Strategy for SimpleSMAStrategy {
    fn on_market_data(&mut self, data: &MarketUpdate) -> Vec<Signal> {
        // Count calls
        static mut CALL_COUNT: usize = 0;
        unsafe {
            CALL_COUNT += 1;
            if CALL_COUNT % 100 == 1 {
                eprintln!("SimpleSMAStrategy.on_market_data called {} times", CALL_COUNT);
            }
        }
        
        let mut signals = Vec::new();
        
        // Check if it's bar data
        if let crate::MarketDataType::Bar(bar) = &data.data {
            let symbol = &data.symbol;
            let price = bar.close;
            
            // Update price history
            let history = self.price_history.entry(symbol.clone()).or_insert_with(Vec::new);
            history.push(price);
            
            // Debug: Log first few prices
            if history.len() <= 3 {
                eprintln!("Price history for {}: {:?}", symbol, history);
            } else if history.len() == 10 || history.len() == 15 {
                eprintln!("Price history length for {}: {} bars", symbol, history.len());
            }
            
            // Keep only necessary history (need one extra for previous SMA calculation)
            if history.len() > self.slow_period + 1 {
                history.remove(0);
            }
            
            // Need enough data
            if history.len() >= self.slow_period {
                // Debug when we first have enough data
                if history.len() == self.slow_period {
                    eprintln!("Now have enough data for {}: {} bars", symbol, history.len());
                }
                // Calculate SMAs
                let fast_sma = history[history.len() - self.fast_period..].iter().sum::<f64>() / self.fast_period as f64;
                let slow_sma = history.iter().sum::<f64>() / history.len() as f64;
                
                // Debug: Log SMAs periodically
                if history.len() % 10 == 0 || (history.len() > self.slow_period && history.len() < self.slow_period + 5) {
                    eprintln!("SMAs for {}: fast={:.2}, slow={:.2}, price={:.2}, history_len={}", 
                        symbol, fast_sma, slow_sma, price, history.len());
                    
                    // Also log if they're close to crossing
                    let diff = (fast_sma - slow_sma).abs();
                    let pct_diff = diff / slow_sma * 100.0;
                    if pct_diff < 1.0 {
                        eprintln!("  -> SMAs are close! Difference: {:.4} ({:.2}%)", diff, pct_diff);
                    }
                }
                
                // Previous SMAs (if we have enough history)
                if history.len() > self.slow_period {
                    // Debug: First time checking for crossovers
                    if history.len() == self.slow_period + 1 {
                        eprintln!("Starting crossover checks for {}", symbol);
                    }
                    let prev_history = &history[..history.len() - 1];
                    let prev_fast_sma = prev_history[prev_history.len() - self.fast_period..].iter().sum::<f64>() / self.fast_period as f64;
                    let prev_slow_sma = prev_history.iter().sum::<f64>() / prev_history.len() as f64;
                    
                    let current_position = self.positions.get(symbol).copied().unwrap_or(0.0);
                    
                    // Golden cross - buy signal
                    if prev_fast_sma <= prev_slow_sma && fast_sma > slow_sma && current_position <= 0.0 {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: crate::backtest::SignalType::Buy,
                            strength: 1.0,
                            quantity: Some(100.0), // Fixed quantity for testing
                            reason: Some("Golden cross".to_string()),
                            metadata: None,
                        });
                        self.positions.insert(symbol.clone(), 1.0);
                        eprintln!("Generated BUY signal for {} at price {}", symbol, price);
                    }
                    
                    // Death cross - sell signal
                    else if prev_fast_sma >= prev_slow_sma && fast_sma < slow_sma && current_position >= 0.0 {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: crate::backtest::SignalType::Sell,
                            strength: 1.0,
                            quantity: Some(100.0), // Fixed quantity for testing
                            reason: Some("Death cross".to_string()),
                            metadata: None,
                        });
                        self.positions.insert(symbol.clone(), -1.0);
                        eprintln!("Generated SELL signal for {} at price {}", symbol, price);
                    }
                }
            } else {
                // Debug: Log when we don't have enough data
                if history.len() == 1 || history.len() == 10 || history.len() == 20 {
                    eprintln!("Not enough data for {}: {} bars (need {})", symbol, history.len(), self.slow_period);
                }
            }
        }
        
        signals
    }
    
    fn on_fill(&mut self, symbol: &str, quantity: f64, price: f64, side: &str) {
        eprintln!("🔸 SMA Strategy - Fill received: {} {} @ ${:.2} - {}", quantity, symbol, price, side);
        let current_pos = self.positions.get(symbol).copied().unwrap_or(0.0);
        let new_pos = if side == "buy" { current_pos + quantity } else { current_pos - quantity };
        
        eprintln!("   Position change: {} -> {}", current_pos, new_pos);
        
        if new_pos.abs() < 0.0001 {
            self.positions.remove(symbol);
            eprintln!("   Position closed");
        } else {
            self.positions.insert(symbol.to_string(), new_pos);
        }
    }
    
    fn get_name(&self) -> &str {
        &self.name
    }
    
    fn get_parameters(&self) -> serde_json::Value {
        serde_json::json!({
            "fast_period": self.fast_period,
            "slow_period": self.slow_period
        })
    }
}

// Error handling for threadsafe functions
struct ErrorStrategy;

impl From<napi::Error> for ErrorStrategy {
    fn from(_e: napi::Error) -> Self {
        ErrorStrategy
    }
}

// Helper to convert NAPI parameters to JSON
fn napi_params_to_json(obj: napi::JsObject) -> Result<serde_json::Value> {
    // For now, just extract the common parameters
    let fast_period = obj.get_named_property::<f64>("fastPeriod").unwrap_or(5.0);
    let slow_period = obj.get_named_property::<f64>("slowPeriod").unwrap_or(15.0);
    
    Ok(serde_json::json!({
        "fastPeriod": fast_period,
        "slowPeriod": slow_period
    }))
}