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

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

/// Momentum Strategy
/// 
/// This strategy trades based on momentum indicators like rate of change (ROC) and
/// relative strength. It aims to capture trends by buying securities showing 
/// upward momentum and selling those showing downward momentum.
/// 
/// Entry Signals:
/// - BUY when momentum crosses above threshold and accelerating
/// - SELL when momentum crosses below -threshold and decelerating
/// 
/// Exit Signals:
/// - Exit long when momentum turns negative
/// - Exit short when momentum turns positive
pub struct MomentumStrategy {
    name: String,
    id: String,
    lookback_period: usize,
    momentum_threshold: f64,
    position_size: f64,
    use_acceleration: bool,
    
    // State
    price_history: HashMap<String, Vec<f64>>,
    momentum_history: HashMap<String, Vec<f64>>,
    positions: HashMap<String, f64>,
}

impl MomentumStrategy {
    pub fn new(
        name: String, 
        id: String,
        lookback_period: usize,
        momentum_threshold: f64,
        position_size: f64,
    ) -> Self {
        Self {
            name,
            id,
            lookback_period,
            momentum_threshold,
            position_size,
            use_acceleration: true,
            price_history: HashMap::new(),
            momentum_history: HashMap::new(),
            positions: HashMap::new(),
        }
    }
    
    fn calculate_momentum(prices: &[f64], lookback_period: usize) -> f64 {
        if prices.len() < 2 {
            return 0.0;
        }
        
        let current = prices.last().unwrap();
        let past = prices[prices.len() - lookback_period.min(prices.len())];
        
        ((current - past) / past) * 100.0
    }
    
    fn calculate_acceleration(momentum_values: &[f64]) -> f64 {
        if momentum_values.len() < 2 {
            return 0.0;
        }
        
        let current = momentum_values.last().unwrap();
        let previous = momentum_values[momentum_values.len() - 2];
        
        current - previous
    }
}

impl Strategy for MomentumStrategy {
    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 reasonable history
            if history.len() > self.lookback_period * 2 {
                history.remove(0);
            }
            
            // Need enough data
            if history.len() >= self.lookback_period {
                // Calculate momentum
                let momentum = Self::calculate_momentum(history, self.lookback_period);
                
                // Update momentum history
                let mom_history = self.momentum_history.entry(symbol.clone()).or_insert_with(Vec::new);
                mom_history.push(momentum);
                
                if mom_history.len() > 5 {
                    mom_history.remove(0);
                }
                
                // Calculate acceleration if enabled
                let acceleration = if self.use_acceleration && mom_history.len() >= 2 {
                    Self::calculate_acceleration(mom_history)
                } else {
                    0.0
                };
                
                let current_position = self.positions.get(symbol).copied().unwrap_or(0.0);
                
                // Check for entry signals
                if current_position == 0.0 {
                    // Long entry: strong positive momentum and accelerating
                    if momentum > self.momentum_threshold && 
                       (!self.use_acceleration || acceleration > 0.0) {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Buy,
                            strength: momentum / 100.0, // Normalize strength
                            quantity: Some(self.position_size),
                            reason: Some(format!(
                                "Momentum buy: momentum {:.2}% > threshold {:.2}%, accel: {:.2}",
                                momentum, self.momentum_threshold, acceleration
                            )),
                            metadata: Some(json!({
                                "momentum": momentum,
                                "acceleration": acceleration,
                                "price": price,
                            })),
                        });
                        self.positions.insert(symbol.clone(), self.position_size);
                    }
                    // Short entry: strong negative momentum and decelerating
                    else if momentum < -self.momentum_threshold && 
                            (!self.use_acceleration || acceleration < 0.0) {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Sell,
                            strength: momentum.abs() / 100.0,
                            quantity: Some(self.position_size),
                            reason: Some(format!(
                                "Momentum sell: momentum {:.2}% < threshold -{:.2}%, accel: {:.2}",
                                momentum, self.momentum_threshold, acceleration
                            )),
                            metadata: Some(json!({
                                "momentum": momentum,
                                "acceleration": acceleration,
                                "price": price,
                            })),
                        });
                        self.positions.insert(symbol.clone(), -self.position_size);
                    }
                }
                // Check for exit signals
                else if current_position > 0.0 {
                    // Exit long when momentum turns negative
                    if momentum < 0.0 {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Sell,
                            strength: 1.0,
                            quantity: Some(current_position),
                            reason: Some(format!(
                                "Momentum exit long: momentum turned negative {:.2}%",
                                momentum
                            )),
                            metadata: Some(json!({
                                "momentum": momentum,
                                "price": price,
                                "exit_type": "momentum_reversal",
                            })),
                        });
                        self.positions.remove(symbol);
                    }
                } else if current_position < 0.0 {
                    // Exit short when momentum turns positive
                    if momentum > 0.0 {
                        signals.push(Signal {
                            symbol: symbol.clone(),
                            signal_type: SignalType::Buy,
                            strength: 1.0,
                            quantity: Some(current_position.abs()),
                            reason: Some(format!(
                                "Momentum exit short: momentum turned positive {:.2}%",
                                momentum
                            )),
                            metadata: Some(json!({
                                "momentum": momentum,
                                "price": price,
                                "exit_type": "momentum_reversal",
                            })),
                        });
                        self.positions.remove(symbol);
                    }
                }
            }
        }
        
        signals
    }
    
    fn on_fill(&mut self, symbol: &str, quantity: f64, price: f64, side: &str) {
        eprintln!("Momentum 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,
            "momentum_threshold": self.momentum_threshold,
            "position_size": self.position_size,
            "use_acceleration": self.use_acceleration,
        })
    }
}