use std::collections::HashMap;
use crate::orderbook::analytics::{OrderBookAnalytics, LiquidityProfile};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BetSizingParameters {
    pub signal_strength: f64,      // -1 to 1
    pub signal_confidence: f64,    // 0 to 1
    pub market_regime: MarketRegime,
    pub volatility: f64,
    pub liquidity_score: f64,
    pub correlation_exposure: f64,
    pub current_drawdown: f64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MarketRegime {
    Trending,
    RangeBound,
    HighVolatility,
    LowVolatility,
    Transitioning,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionSize {
    pub shares: u32,
    pub notional_value: f64,
    pub percent_of_capital: f64,
    pub risk_adjusted_size: f64,
    pub sizing_method: String,
    pub adjustments: Vec<SizeAdjustment>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SizeAdjustment {
    pub reason: String,
    pub factor: f64,
}

pub struct BetSizer {
    capital: f64,
    base_risk_per_trade: f64,
    max_position_size: f64,
    min_position_size: f64,
    use_kelly: bool,
    volatility_scaling: bool,
    regime_adjustments: HashMap<MarketRegime, f64>,
}

impl BetSizer {
    pub fn new(capital: f64, base_risk_per_trade: f64) -> Self {
        let mut regime_adjustments = HashMap::new();
        regime_adjustments.insert(MarketRegime::Trending, 1.2);
        regime_adjustments.insert(MarketRegime::RangeBound, 0.8);
        regime_adjustments.insert(MarketRegime::HighVolatility, 0.6);
        regime_adjustments.insert(MarketRegime::LowVolatility, 1.1);
        regime_adjustments.insert(MarketRegime::Transitioning, 0.7);

        Self {
            capital,
            base_risk_per_trade,
            max_position_size: 0.10, // 10% max
            min_position_size: 0.001, // 0.1% min
            use_kelly: true,
            volatility_scaling: true,
            regime_adjustments,
        }
    }

    pub fn calculate_position_size(
        &self,
        params: &BetSizingParameters,
        price: f64,
        stop_loss: Option<f64>,
        historical_performance: Option<&PerformanceStats>,
        orderbook_analytics: Option<&OrderBookAnalytics>,
        liquidity_profile: Option<&LiquidityProfile>,
    ) -> PositionSize {
        let mut adjustments = Vec::new();
        
        // Start with base position size
        let mut risk_fraction = self.base_risk_per_trade;
        
        // 1. Kelly Criterion adjustment
        if self.use_kelly {
            if let Some(perf) = historical_performance {
                let kelly_fraction = self.calculate_kelly_fraction(perf);
                let kelly_adjustment = (kelly_fraction / self.base_risk_per_trade).min(2.0).max(0.1);
                risk_fraction *= kelly_adjustment;
                adjustments.push(SizeAdjustment {
                    reason: "Kelly Criterion".to_string(),
                    factor: kelly_adjustment,
                });
            }
        }
        
        // 2. Signal strength adjustment
        let signal_adjustment = self.calculate_signal_adjustment(params.signal_strength, params.signal_confidence);
        risk_fraction *= signal_adjustment;
        adjustments.push(SizeAdjustment {
            reason: "Signal Strength".to_string(),
            factor: signal_adjustment,
        });
        
        // 3. Volatility adjustment
        if self.volatility_scaling {
            let vol_adjustment = self.calculate_volatility_adjustment(params.volatility);
            risk_fraction *= vol_adjustment;
            adjustments.push(SizeAdjustment {
                reason: "Volatility Scaling".to_string(),
                factor: vol_adjustment,
            });
        }
        
        // 4. Market regime adjustment
        if let Some(&regime_factor) = self.regime_adjustments.get(&params.market_regime) {
            risk_fraction *= regime_factor;
            adjustments.push(SizeAdjustment {
                reason: format!("Market Regime ({:?})", params.market_regime),
                factor: regime_factor,
            });
        }
        
        // 5. Liquidity adjustment
        let liquidity_adjustment = self.calculate_liquidity_adjustment(
            params.liquidity_score,
            orderbook_analytics,
            liquidity_profile,
        );
        risk_fraction *= liquidity_adjustment;
        adjustments.push(SizeAdjustment {
            reason: "Liquidity".to_string(),
            factor: liquidity_adjustment,
        });
        
        // 6. Correlation adjustment
        let correlation_adjustment = self.calculate_correlation_adjustment(params.correlation_exposure);
        risk_fraction *= correlation_adjustment;
        adjustments.push(SizeAdjustment {
            reason: "Correlation Exposure".to_string(),
            factor: correlation_adjustment,
        });
        
        // 7. Drawdown adjustment
        let drawdown_adjustment = self.calculate_drawdown_adjustment(params.current_drawdown);
        risk_fraction *= drawdown_adjustment;
        adjustments.push(SizeAdjustment {
            reason: "Current Drawdown".to_string(),
            factor: drawdown_adjustment,
        });
        
        // Calculate final position size
        let position_value = if let Some(stop_price) = stop_loss {
            // Risk-based sizing
            let risk_per_share = (price - stop_price).abs();
            let risk_amount = self.capital * risk_fraction;
            risk_amount / risk_per_share
        } else {
            // Fixed percentage sizing
            self.capital * risk_fraction / price
        };
        
        // Apply min/max constraints
        let constrained_value = position_value
            .min(self.capital * self.max_position_size / price)
            .max(self.capital * self.min_position_size / price);
        
        let shares = constrained_value.floor() as u32;
        let notional_value = shares as f64 * price;
        let percent_of_capital = notional_value / self.capital;
        
        PositionSize {
            shares,
            notional_value,
            percent_of_capital,
            risk_adjusted_size: risk_fraction,
            sizing_method: if stop_loss.is_some() { "Risk-based".to_string() } else { "Fixed-percentage".to_string() },
            adjustments,
        }
    }

    fn calculate_kelly_fraction(&self, perf: &PerformanceStats) -> f64 {
        if perf.total_trades < 20 {
            return self.base_risk_per_trade; // Not enough data
        }
        
        let win_rate = perf.win_rate;
        let loss_rate = 1.0 - win_rate;
        
        if perf.avg_loss == 0.0 {
            return self.base_risk_per_trade;
        }
        
        let win_loss_ratio = perf.avg_win / perf.avg_loss.abs();
        
        // Kelly formula: f = p - q/b
        // where p = win probability, q = loss probability, b = win/loss ratio
        let kelly = win_rate - (loss_rate / win_loss_ratio);
        
        // Apply Kelly fraction (typically 25% of full Kelly)
        let kelly_fraction = kelly * 0.25;
        
        // Ensure it's positive and reasonable
        kelly_fraction.max(0.0).min(0.25)
    }

    fn calculate_signal_adjustment(&self, strength: f64, confidence: f64) -> f64 {
        // Strength is -1 to 1, we want 0 to 1
        let normalized_strength = (strength.abs() + 1.0) / 2.0;
        
        // Combine strength and confidence
        let signal_score = normalized_strength * 0.7 + confidence * 0.3;
        
        // Map to adjustment factor (0.5 to 1.5)
        0.5 + signal_score
    }

    fn calculate_volatility_adjustment(&self, volatility: f64) -> f64 {
        // Target volatility (e.g., 15% annualized)
        let target_vol = 0.15 / (252.0_f64.sqrt()); // Daily vol
        
        // Inverse volatility scaling
        let adjustment = (target_vol / volatility).min(1.5).max(0.5);
        
        adjustment
    }

    fn calculate_liquidity_adjustment(
        &self,
        liquidity_score: f64,
        orderbook: Option<&OrderBookAnalytics>,
        profile: Option<&LiquidityProfile>,
    ) -> f64 {
        let mut adjustment = 1.0;
        
        // Base liquidity score adjustment
        if liquidity_score < 0.3 {
            adjustment *= 0.5; // Very poor liquidity
        } else if liquidity_score < 0.5 {
            adjustment *= 0.7;
        } else if liquidity_score > 0.8 {
            adjustment *= 1.1; // Good liquidity bonus
        }
        
        // Orderbook spread adjustment
        if let Some(ob) = orderbook {
            if ob.spread_bps > 50.0 {
                adjustment *= 0.8; // Wide spread penalty
            } else if ob.spread_bps < 10.0 {
                adjustment *= 1.1; // Tight spread bonus
            }
        }
        
        // Market impact consideration
        if let Some(prof) = profile {
            // Check if our typical order size would move the market
            let typical_order_value = self.capital * self.base_risk_per_trade;
            let impact = prof.calculate_market_impact(typical_order_value, true);
            
            if impact.price_impact > 0.001 { // More than 10 bps impact
                adjustment *= (1.0 - impact.price_impact * 10.0).max(0.5);
            }
        }
        
        adjustment
    }

    fn calculate_correlation_adjustment(&self, correlation_exposure: f64) -> f64 {
        // Reduce size if highly correlated with existing positions
        if correlation_exposure > 0.7 {
            0.5
        } else if correlation_exposure > 0.5 {
            0.7
        } else if correlation_exposure > 0.3 {
            0.9
        } else {
            1.0
        }
    }

    fn calculate_drawdown_adjustment(&self, current_drawdown: f64) -> f64 {
        // Reduce size during drawdowns
        if current_drawdown > 0.20 {
            0.5 // 50% reduction if in 20%+ drawdown
        } else if current_drawdown > 0.10 {
            0.7
        } else if current_drawdown > 0.05 {
            0.85
        } else {
            1.0
        }
    }

    pub fn calculate_optimal_stop_loss(
        &self,
        entry_price: f64,
        volatility: f64,
        support_levels: &[f64],
        atr: Option<f64>,
        is_long: bool,
    ) -> f64 {
        let mut stop_candidates = Vec::new();
        
        // 1. Volatility-based stop
        let vol_stop = if is_long {
            entry_price * (1.0 - 2.0 * volatility)
        } else {
            entry_price * (1.0 + 2.0 * volatility)
        };
        stop_candidates.push(("Volatility", vol_stop));
        
        // 2. ATR-based stop
        if let Some(atr_value) = atr {
            let atr_stop = if is_long {
                entry_price - 2.0 * atr_value
            } else {
                entry_price + 2.0 * atr_value
            };
            stop_candidates.push(("ATR", atr_stop));
        }
        
        // 3. Support/Resistance based stop
        if !support_levels.is_empty() {
            let technical_stop = if is_long {
                // Find nearest support below entry
                support_levels.iter()
                    .filter(|&&level| level < entry_price)
                    .max_by(|a, b| a.partial_cmp(b).unwrap())
                    .map(|&level| level * 0.995) // Just below support
            } else {
                // Find nearest resistance above entry
                support_levels.iter()
                    .filter(|&&level| level > entry_price)
                    .min_by(|a, b| a.partial_cmp(b).unwrap())
                    .map(|&level| level * 1.005) // Just above resistance
            };
            
            if let Some(stop) = technical_stop {
                stop_candidates.push(("Technical", stop));
            }
        }
        
        // 4. Maximum loss stop (e.g., 5% from entry)
        let max_loss_stop = if is_long {
            entry_price * 0.95
        } else {
            entry_price * 1.05
        };
        stop_candidates.push(("MaxLoss", max_loss_stop));
        
        // Choose the most conservative stop (closest to entry)
        let optimal_stop = if is_long {
            stop_candidates.iter()
                .map(|(_, stop)| *stop)
                .max_by(|a, b| a.partial_cmp(b).unwrap())
                .unwrap_or(vol_stop)
        } else {
            stop_candidates.iter()
                .map(|(_, stop)| *stop)
                .min_by(|a, b| a.partial_cmp(b).unwrap())
                .unwrap_or(vol_stop)
        };
        
        optimal_stop
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceStats {
    pub total_trades: u32,
    pub win_rate: f64,
    pub avg_win: f64,
    pub avg_loss: f64,
    pub sharpe_ratio: f64,
    pub max_consecutive_losses: u32,
}

/// Dynamic position sizing based on market conditions
pub struct DynamicSizer {
    base_sizer: BetSizer,
    regime_detector: MarketRegimeDetector,
    performance_tracker: PerformanceTracker,
}

impl DynamicSizer {
    pub fn new(capital: f64, base_risk: f64) -> Self {
        Self {
            base_sizer: BetSizer::new(capital, base_risk),
            regime_detector: MarketRegimeDetector::new(),
            performance_tracker: PerformanceTracker::new(),
        }
    }

    pub fn update_market_data(&mut self, volatility: f64, volume: f64, trend_strength: f64) {
        self.regime_detector.update(volatility, volume, trend_strength);
    }

    pub fn record_trade(&mut self, pnl: f64, holding_period: i64) {
        self.performance_tracker.record_trade(pnl, holding_period);
    }

    pub fn get_sizing_parameters(&self) -> BetSizingParameters {
        BetSizingParameters {
            signal_strength: 0.0, // To be filled by strategy
            signal_confidence: 0.0, // To be filled by strategy
            market_regime: self.regime_detector.current_regime(),
            volatility: self.regime_detector.current_volatility(),
            liquidity_score: 0.0, // To be filled from orderbook
            correlation_exposure: 0.0, // To be filled from portfolio
            current_drawdown: self.performance_tracker.current_drawdown(),
        }
    }
}

struct MarketRegimeDetector {
    volatility_history: Vec<f64>,
    volume_history: Vec<f64>,
    trend_history: Vec<f64>,
    window_size: usize,
}

impl MarketRegimeDetector {
    fn new() -> Self {
        Self {
            volatility_history: Vec::new(),
            volume_history: Vec::new(),
            trend_history: Vec::new(),
            window_size: 20,
        }
    }

    fn update(&mut self, volatility: f64, volume: f64, trend_strength: f64) {
        self.volatility_history.push(volatility);
        self.volume_history.push(volume);
        self.trend_history.push(trend_strength);

        // Keep only recent history
        if self.volatility_history.len() > self.window_size {
            self.volatility_history.remove(0);
            self.volume_history.remove(0);
            self.trend_history.remove(0);
        }
    }

    fn current_regime(&self) -> MarketRegime {
        if self.volatility_history.len() < 5 {
            return MarketRegime::Transitioning;
        }

        let avg_vol = self.volatility_history.iter().sum::<f64>() / self.volatility_history.len() as f64;
        let avg_trend = self.trend_history.iter().sum::<f64>() / self.trend_history.len() as f64;

        if avg_vol > 0.02 {
            MarketRegime::HighVolatility
        } else if avg_vol < 0.01 {
            MarketRegime::LowVolatility
        } else if avg_trend.abs() > 0.7 {
            MarketRegime::Trending
        } else if avg_trend.abs() < 0.3 {
            MarketRegime::RangeBound
        } else {
            MarketRegime::Transitioning
        }
    }

    fn current_volatility(&self) -> f64 {
        if self.volatility_history.is_empty() {
            return 0.015; // Default 1.5% daily vol
        }
        self.volatility_history.iter().sum::<f64>() / self.volatility_history.len() as f64
    }
}

struct PerformanceTracker {
    trades: Vec<(f64, i64)>, // (pnl, holding_period)
    peak_capital: f64,
    current_capital: f64,
}

impl PerformanceTracker {
    fn new() -> Self {
        Self {
            trades: Vec::new(),
            peak_capital: 100000.0,
            current_capital: 100000.0,
        }
    }

    fn record_trade(&mut self, pnl: f64, holding_period: i64) {
        self.trades.push((pnl, holding_period));
        self.current_capital += pnl;
        if self.current_capital > self.peak_capital {
            self.peak_capital = self.current_capital;
        }
    }

    fn current_drawdown(&self) -> f64 {
        if self.peak_capital > 0.0 {
            (self.peak_capital - self.current_capital) / self.peak_capital
        } else {
            0.0
        }
    }
}