use std::collections::HashMap;
use chrono::{DateTime, Utc};
use serde_json::json;

use crate::{
    MarketUpdate, MarketDataType, 
    backtest::{Strategy, Signal, SignalType},
};

/// Mean Reversion Strategy
/// 
/// This strategy identifies when a security's price deviates significantly from its 
/// moving average and trades on the assumption that it will revert back to the mean.
/// 
/// Entry Signals:
/// - BUY when price falls below (MA - threshold * std_dev)
/// - SELL when price rises above (MA + threshold * std_dev)
/// 
/// Exit Signals:
/// - Exit long when price reaches MA
/// - Exit short when price reaches MA
pub struct MeanReversionStrategy {
    name: String,
    id: String,
    lookback_period: usize,
    entry_threshold: f64,  // Number of standard deviations
    exit_threshold: f64,   // Typically 0 (at the mean)
    position_size: f64,
    
    // State
    price_history: HashMap<String, Vec<f64>>,
    positions: HashMap<String, f64>,
}

impl MeanReversionStrategy {
    pub fn new(
        name: String, 
        id: String,
        lookback_period: usize,
        entry_threshold: f64,
        position_size: f64,
    ) -> Self {
        Self {
            name,
            id,
            lookback_period,
            entry_threshold,
            exit_threshold: 0.0, // Exit at the mean
            position_size,
            price_history: HashMap::new(),
            positions: HashMap::new(),
        }
    }
    
    fn calculate_mean(prices: &[f64]) -> f64 {
        prices.iter().sum::<f64>() / prices.len() as f64
    }
    
    fn calculate_std_dev(prices: &[f64], mean: f64) -> f64 {
        let variance = prices.iter()
            .map(|p| (p - mean).powi(2))
            .sum::<f64>() / prices.len() as f64;
        variance.sqrt()
    }
}

impl Strategy for MeanReversionStrategy {
    fn on_market_data(&mut self, data: &MarketUpdate) -> Vec<Signal> {
        let mut signals = Vec::new();
        
        // Only process bar data
        if let 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);
            
            // Keep only necessary history
            if history.len() > self.lookback_period {
                history.remove(0);
            }
            
            // Need enough data
            if history.len() >= self.lookback_period {
                // Calculate statistics
                let mean = Self::calculate_mean(history);
                let std_dev = Self::calculate_std_dev(history, mean);
                
                // Calculate bands
                let upper_band = mean + self.entry_threshold * std_dev;
                let lower_band = mean - self.entry_threshold * std_dev;
                
                let current_position = self.positions.get(symbol).copied().unwrap_or(0.0);
                
                // Check for entry signals
                if current_position == 0.0 {
                    if price < lower_band {
                        // Price is oversold, buy
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Buy,
                            strength: 1.0,
                            quantity: Some(self.position_size),
                            reason: Some(format!(
                                "Mean reversion buy: price ${:.2} < lower band ${:.2} (mean: ${:.2}, std: ${:.2})",
                                price, lower_band, mean, std_dev
                            )),
                            metadata: Some(json!({
                                "mean": mean,
                                "std_dev": std_dev,
                                "upper_band": upper_band,
                                "lower_band": lower_band,
                                "price": price,
                            })),
                        });
                        self.positions.insert(symbol.clone(), self.position_size);
                    } else if price > upper_band {
                        // Price is overbought, sell short
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Sell,
                            strength: 1.0,
                            quantity: Some(self.position_size),
                            reason: Some(format!(
                                "Mean reversion sell: price ${:.2} > upper band ${:.2} (mean: ${:.2}, std: ${:.2})",
                                price, upper_band, mean, std_dev
                            )),
                            metadata: Some(json!({
                                "mean": mean,
                                "std_dev": std_dev,
                                "upper_band": upper_band,
                                "lower_band": lower_band,
                                "price": price,
                            })),
                        });
                        self.positions.insert(symbol.clone(), -self.position_size);
                    }
                }
                // Check for exit signals
                else if current_position > 0.0 {
                    // We're long, exit when price crosses above mean (not just touches)
                    // or when we hit stop loss
                    let stop_loss = lower_band - std_dev; // Stop loss below lower band
                    if price >= mean * 1.01 || price <= stop_loss {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Sell,
                            strength: 1.0,
                            quantity: Some(current_position),
                            reason: Some(format!(
                                "Mean reversion exit long: price ${:.2} {} mean ${:.2}",
                                price, if price >= mean * 1.01 { "crossed above" } else { "hit stop loss below" }, mean
                            )),
                            metadata: Some(json!({
                                "mean": mean,
                                "price": price,
                                "exit_type": "mean_reversion",
                            })),
                        });
                        self.positions.remove(symbol);
                    }
                } else if current_position < 0.0 {
                    // We're short, exit when price crosses below mean
                    // or when we hit stop loss
                    let stop_loss = upper_band + std_dev; // Stop loss above upper band
                    if price <= mean * 0.99 || price >= stop_loss {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Buy,
                            strength: 1.0,
                            quantity: Some(current_position.abs()),
                            reason: Some(format!(
                                "Mean reversion exit short: price ${:.2} {} mean ${:.2}",
                                price, if price <= mean * 0.99 { "crossed below" } else { "hit stop loss above" }, mean
                            )),
                            metadata: Some(json!({
                                "mean": mean,
                                "price": price,
                                "exit_type": "mean_reversion",
                            })),
                        });
                        self.positions.remove(symbol);
                    }
                }
            }
        }
        
        signals
    }
    
    fn on_fill(&mut self, symbol: &str, quantity: f64, price: f64, side: &str) {
        // Position tracking is handled in on_market_data for simplicity
        eprintln!("Mean reversion fill: {} {} @ {} - {}", quantity, symbol, price, side);
    }
    
    fn get_name(&self) -> &str {
        &self.name
    }
    
    fn get_parameters(&self) -> serde_json::Value {
        json!({
            "lookback_period": self.lookback_period,
            "entry_threshold": self.entry_threshold,
            "exit_threshold": self.exit_threshold,
            "position_size": self.position_size,
        })
    }
}