//! Technical Indicator Calculations for ML Feature Extraction
//!
//! This module provides stateful, high-performance technical indicator calculations
//! for ML training data pipelines. All indicators support incremental updates with
//! O(1) amortized complexity for HFT requirements.
//!
//! ## Supported Indicators (36 Total)
//!
//! ### Original Indicators (16)
//! - **RSI (Relative Strength Index)**: 14-period momentum oscillator (0-100 range)
//! - **EMA (Exponential Moving Average)**: Fast (12) and slow (26) EMAs
//! - **MACD (Moving Average Convergence Divergence)**: Trend following indicator
//! - **Bollinger Bands**: Volatility indicator with SMA ± 2σ (middle, upper, lower, width)
//! - **ATR (Average True Range)**: Volatility measurement
//!
//! ### NEW Momentum Indicators (3)
//! - **MFI (Money Flow Index)**: Volume-weighted RSI (0-100, overbought >80, oversold <20)
//! - **CMF (Chaikin Money Flow)**: Accumulation/distribution indicator (-1 to +1, buying pressure)
//! - **Chaikin Oscillator**: Fast/slow EMA of A/D Line (trend strength)
//!
//! ### NEW Volatility Indicators (8)
//! - **Keltner Channels**: EMA ± ATR multiplier (middle, upper, lower, width)
//! - **Donchian Channels**: Highest high/lowest low (middle, upper, lower, width)
//!
//! ### NEW Volume Indicators (5)
//! - **OBV (On-Balance Volume)**: Cumulative volume direction indicator
//! - **VWAP (Volume-Weighted Average Price)**: Intraday benchmark price
//! - **VWAP Deviation**: Price distance from VWAP (%)
//! - **Volume Oscillator**: Fast/slow EMA of volume (%)
//!
//! ### Feature Count Breakdown
//! - OHLCV: 5 features (open, high, low, close, volume)
//! - Original indicators: 11 features (RSI, EMA×2, MACD×3, Bollinger×4, ATR)
//! - New momentum: 3 features (MFI, CMF, Chaikin)
//! - New volatility: 8 features (Keltner×4, Donchian×4)
//! - New volume: 4 features (OBV, VWAP, VWAP deviation, Vol Oscillator)
//! - Time-based: 5 features (hour, day, month, is_market_hours, time_since_open)
//! **Total: 36 features** (16 → 36, +125% increase)
//!
//! ## Architecture
//!
//! ```rust
//! use ml_training_service::technical_indicators::TechnicalIndicatorCalculator;
//! use ml_training_service::technical_indicators::IndicatorConfig;
//!
//! let config = IndicatorConfig::default();
//! let mut calculator = TechnicalIndicatorCalculator::new("AAPL".to_string(), config);
//!
//! // Feed price data
//! calculator.update(100.0, 50000.0);  // price, volume
//!
//! // Extract indicators
//! let indicators = calculator.current_indicators();
//! ```

use std::collections::VecDeque;

/// Configuration for technical indicator calculations
#[derive(Debug, Clone)]
pub struct IndicatorConfig {
    /// `RSI` period (default: 14)
    pub rsi_period: usize,
    /// Fast `EMA` period (default: 12)
    pub ema_fast_period: usize,
    /// Slow `EMA` period (default: 26)
    pub ema_slow_period: usize,
    /// `MACD` signal line period (default: 9)
    pub macd_signal_period: usize,
    /// Bollinger Bands period (default: 20)
    pub bollinger_period: usize,
    /// Bollinger Bands standard deviations (default: 2.0)
    pub bollinger_std_dev: f64,
    /// `ATR` period (default: 14)
    pub atr_period: usize,
    /// Minimum data points before calculating indicators
    pub warmup_period: usize,

    // === NEW MOMENTUM INDICATORS ===
    /// Money Flow Index period (default: 14)
    pub mfi_period: usize,
    /// Chaikin Money Flow period (default: 20)
    pub cmf_period: usize,
    /// Chaikin Oscillator fast/slow periods (default: 3, 10)
    pub chaikin_fast_period: usize,
    pub chaikin_slow_period: usize,

    // === NEW VOLATILITY INDICATORS ===
    /// Keltner Channel period (default: 20)
    pub keltner_period: usize,
    /// Keltner Channel multiplier (default: 2.0)
    pub keltner_multiplier: f64,
    /// Donchian Channel period (default: 20)
    pub donchian_period: usize,

    // === NEW VOLUME INDICATORS ===
    /// VWAP reset period in bars (default: 390 for daily)
    pub vwap_reset_period: usize,
    /// Volume Oscillator fast/slow periods (default: 5, 10)
    pub vol_osc_fast_period: usize,
    pub vol_osc_slow_period: usize,
}

impl Default for IndicatorConfig {
    fn default() -> Self {
        Self {
            rsi_period: 14,
            ema_fast_period: 12,
            ema_slow_period: 26,
            macd_signal_period: 9,
            bollinger_period: 20,
            bollinger_std_dev: 2.0,
            atr_period: 14,
            warmup_period: 26, // Max of all periods for full indicator calculation

            // New momentum indicators
            mfi_period: 14,
            cmf_period: 20,
            chaikin_fast_period: 3,
            chaikin_slow_period: 10,

            // New volatility indicators
            keltner_period: 20,
            keltner_multiplier: 2.0,
            donchian_period: 20,

            // New volume indicators
            vwap_reset_period: 390, // ~1 trading day (6.5 hours)
            vol_osc_fast_period: 5,
            vol_osc_slow_period: 10,
        }
    }
}

/// Stateful technical indicator calculator
///
/// Maintains rolling windows and state for efficient indicator calculation.
///
/// Designed for O(1) amortized updates with minimal allocations.
pub struct TechnicalIndicatorCalculator {
    /// Trading symbol this calculator tracks
    symbol: String,

    /// Configuration
    config: IndicatorConfig,

    /// Price history for windowed calculations
    price_history: VecDeque<f64>,

    /// Volume history
    volume_history: VecDeque<f64>,

    /// High prices for ATR
    high_history: VecDeque<f64>,

    /// Low prices for ATR
    low_history: VecDeque<f64>,

    /// `RSI` internal state
    rsi_state: RsiState,

    /// `EMA` states
    ema_fast_state: Option<f64>,
    ema_slow_state: Option<f64>,

    /// `MACD` signal line state
    macd_signal_state: Option<f64>,

    /// `ATR` state
    atr_state: Option<f64>,

    /// Total updates received
    update_count: usize,

    // === NEW INDICATOR STATES ===
    /// Money Flow Index state
    mfi_state: MFIState,

    /// Chaikin Money Flow accumulator
    cmf_accumulator: f64,
    cmf_volume_sum: f64,

    /// Accumulation/Distribution Line for Chaikin Oscillator
    ad_line: f64,
    chaikin_fast_ema: Option<f64>,
    chaikin_slow_ema: Option<f64>,

    /// Donchian Channel state (highest high, lowest low)
    donchian_highs: VecDeque<f64>,
    donchian_lows: VecDeque<f64>,

    /// On-Balance Volume state
    obv: f64,

    /// VWAP state (cumulative price * volume, cumulative volume)
    vwap_cumulative_pv: f64,
    vwap_cumulative_volume: f64,
    vwap_bar_count: usize,

    /// Volume EMA states for Volume Oscillator
    vol_ema_fast: Option<f64>,
    vol_ema_slow: Option<f64>,
}

/// `RSI` calculator state using Wilder's smoothing
#[derive(Debug, Clone)]
struct RsiState {
    /// Average gain (Wilder's smoothed)
    avg_gain: f64,
    /// Average loss (Wilder's smoothed)
    avg_loss: f64,
    /// Previous price for change calculation
    prev_price: Option<f64>,
    /// Initialization complete flag
    initialized: bool,
}

impl Default for RsiState {
    fn default() -> Self {
        Self {
            avg_gain: 0.0,
            avg_loss: 0.0,
            prev_price: None,
            initialized: false,
        }
    }
}

/// Money Flow Index state
#[derive(Debug, Clone)]
struct MFIState {
    /// Positive money flow sum
    positive_mf: f64,
    /// Negative money flow sum
    negative_mf: f64,
    /// Previous typical price
    prev_typical_price: Option<f64>,
    /// Initialization complete
    initialized: bool,
}

impl Default for MFIState {
    fn default() -> Self {
        Self {
            positive_mf: 0.0,
            negative_mf: 0.0,
            prev_typical_price: None,
            initialized: false,
        }
    }
}

impl TechnicalIndicatorCalculator {
    /// Get the trading symbol this calculator is tracking
    pub fn symbol(&self) -> &str {
        &self.symbol
    }

    /// Create a new technical indicator calculator
    ///
    /// # Arguments
    ///
    /// * `symbol` - Trading symbol
    /// * `config` - Indicator configuration
    pub fn new(symbol: String, config: IndicatorConfig) -> Self {
        let max_window = config
            .warmup_period
            .max(config.bollinger_period)
            .max(config.keltner_period)
            .max(config.donchian_period)
            .max(config.cmf_period);

        Self {
            symbol,
            config,
            price_history: VecDeque::with_capacity(max_window),
            volume_history: VecDeque::with_capacity(max_window),
            high_history: VecDeque::with_capacity(max_window),
            low_history: VecDeque::with_capacity(max_window),
            rsi_state: RsiState::default(),
            ema_fast_state: None,
            ema_slow_state: None,
            macd_signal_state: None,
            atr_state: None,
            update_count: 0,

            // Initialize new indicator states
            mfi_state: MFIState::default(),
            cmf_accumulator: 0.0,
            cmf_volume_sum: 0.0,
            ad_line: 0.0,
            chaikin_fast_ema: None,
            chaikin_slow_ema: None,
            donchian_highs: VecDeque::with_capacity(max_window),
            donchian_lows: VecDeque::with_capacity(max_window),
            obv: 0.0,
            vwap_cumulative_pv: 0.0,
            vwap_cumulative_volume: 0.0,
            vwap_bar_count: 0,
            vol_ema_fast: None,
            vol_ema_slow: None,
        }
    }

    /// Update calculator with new `OHLC` data
    ///
    /// # Arguments
    ///
    /// * `price` - Current price (close)
    /// * `volume` - Current volume
    ///
    /// * `high` - High price (optional, defaults to price)
    /// * `low` - Low price (optional, defaults to price)
    pub fn update(&mut self, price: f64, volume: f64, high: Option<f64>, low: Option<f64>) {
        // Add to history with window management
        self.price_history.push_back(price);
        self.volume_history.push_back(volume);
        self.high_history.push_back(high.unwrap_or(price));
        self.low_history.push_back(low.unwrap_or(price));

        // Maintain maximum window size
        let max_window = self.config.warmup_period.max(self.config.bollinger_period);
        if self.price_history.len() > max_window {
            self.price_history.pop_front();
            self.volume_history.pop_front();
            self.high_history.pop_front();
            self.low_history.pop_front();
        }

        self.update_count += 1;

        // Update stateful indicators incrementally
        self.update_rsi(price);
        self.update_ema(price);
        self.update_atr();

        // Update new indicators
        self.update_mfi(high.unwrap_or(price), low.unwrap_or(price), price, volume);
        self.update_cmf(high.unwrap_or(price), low.unwrap_or(price), price, volume);
        self.update_chaikin(high.unwrap_or(price), low.unwrap_or(price), price, volume);
        self.update_donchian(high.unwrap_or(price), low.unwrap_or(price));
        self.update_obv(price, volume);
        self.update_vwap(price, volume);
        self.update_volume_oscillator(volume);
    }

    /// Update `RSI` using Wilder's smoothing method
    fn update_rsi(&mut self, price: f64) {
        if let Some(prev) = self.rsi_state.prev_price {
            let change = price - prev;
            let gain = if change > 0.0 { change } else { 0.0 };
            let loss = if change < 0.0 { -change } else { 0.0 };

            if !self.rsi_state.initialized && self.update_count >= self.config.rsi_period {
                // Initial average using SMA
                let gains: Vec<f64> = self
                    .price_history
                    .iter()
                    .zip(self.price_history.iter().skip(1))
                    .map(|(p1, p2)| {
                        let change = p2 - p1;
                        if change > 0.0 {
                            change
                        } else {
                            0.0
                        }
                    })
                    .collect();

                let losses: Vec<f64> = self
                    .price_history
                    .iter()
                    .zip(self.price_history.iter().skip(1))
                    .map(|(p1, p2)| {
                        let change = p2 - p1;
                        if change < 0.0 {
                            -change
                        } else {
                            0.0
                        }
                    })
                    .collect();

                self.rsi_state.avg_gain = gains.iter().sum::<f64>() / self.config.rsi_period as f64;
                self.rsi_state.avg_loss =
                    losses.iter().sum::<f64>() / self.config.rsi_period as f64;
                self.rsi_state.initialized = true;
            } else if self.rsi_state.initialized {
                // Wilder's smoothing: avg = (prev_avg * (n-1) + current) / n
                let period = self.config.rsi_period as f64;
                self.rsi_state.avg_gain =
                    (self.rsi_state.avg_gain * (period - 1.0) + gain) / period;
                self.rsi_state.avg_loss =
                    (self.rsi_state.avg_loss * (period - 1.0) + loss) / period;
            }
        }

        self.rsi_state.prev_price = Some(price);
    }

    /// Update `EMA` states incrementally
    fn update_ema(&mut self, price: f64) {
        // Fast EMA (12-period)
        if let Some(ema) = self.ema_fast_state {
            let k = 2.0 / (self.config.ema_fast_period as f64 + 1.0);
            self.ema_fast_state = Some(price * k + ema * (1.0 - k));
        } else if self.update_count >= self.config.ema_fast_period {
            // Initialize with SMA
            let sum: f64 = self
                .price_history
                .iter()
                .rev()
                .take(self.config.ema_fast_period)
                .sum();
            self.ema_fast_state = Some(sum / self.config.ema_fast_period as f64);
        }

        // Slow EMA (26-period)
        if let Some(ema) = self.ema_slow_state {
            let k = 2.0 / (self.config.ema_slow_period as f64 + 1.0);
            self.ema_slow_state = Some(price * k + ema * (1.0 - k));
        } else if self.update_count >= self.config.ema_slow_period {
            // Initialize with SMA
            let sum: f64 = self
                .price_history
                .iter()
                .rev()
                .take(self.config.ema_slow_period)
                .sum();
            self.ema_slow_state = Some(sum / self.config.ema_slow_period as f64);
        }

        // MACD signal line (9-period EMA of MACD)
        if let Some(macd) = self.calculate_macd() {
            if let Some(signal) = self.macd_signal_state {
                let k = 2.0 / (self.config.macd_signal_period as f64 + 1.0);
                self.macd_signal_state = Some(macd * k + signal * (1.0 - k));
            } else if self.update_count
                >= self.config.ema_slow_period + self.config.macd_signal_period
            {
                self.macd_signal_state = Some(macd);
            }
        }
    }

    /// Update `ATR` (Average True Range)
    fn update_atr(&mut self) {
        if self.update_count < 2 {
            return;
        }

        // Calculate True Range
        let Some(&high) = self.high_history.back() else {
            return;
        };
        let Some(&low) = self.low_history.back() else {
            return;
        };
        let len = self.price_history.len();
        let Some(prev_close) = self.price_history.get(len.saturating_sub(2)) else {
            return;
        };

        let tr = (high - low)
            .max((high - prev_close).abs())
            .max((low - prev_close).abs());

        // Update ATR using Wilder's smoothing
        if let Some(atr) = self.atr_state {
            let period = self.config.atr_period as f64;
            self.atr_state = Some((atr * (period - 1.0) + tr) / period);
        } else if self.update_count >= self.config.atr_period {
            // Initialize with SMA of TR
            self.atr_state = Some(tr);
        }
    }

    // ===== NEW INDICATOR UPDATE METHODS =====

    /// Update Money Flow Index (MFI) - momentum indicator with volume
    fn update_mfi(&mut self, high: f64, low: f64, close: f64, volume: f64) {
        let typical_price = (high + low + close) / 3.0;
        let money_flow = typical_price * volume;

        if let Some(prev_tp) = self.mfi_state.prev_typical_price {
            if typical_price > prev_tp {
                // Positive money flow
                self.mfi_state.positive_mf = self.mfi_state.positive_mf
                    * ((self.config.mfi_period - 1) as f64 / self.config.mfi_period as f64)
                    + money_flow;
            } else if typical_price < prev_tp {
                // Negative money flow
                self.mfi_state.negative_mf = self.mfi_state.negative_mf
                    * ((self.config.mfi_period - 1) as f64 / self.config.mfi_period as f64)
                    + money_flow;
            }

            if self.update_count >= self.config.mfi_period {
                self.mfi_state.initialized = true;
            }
        }

        self.mfi_state.prev_typical_price = Some(typical_price);
    }

    /// Update Chaikin Money Flow (CMF) - accumulates money flow over period
    fn update_cmf(&mut self, high: f64, low: f64, close: f64, volume: f64) {
        let range = high - low;
        if range > 0.0 {
            let multiplier = ((close - low) - (high - close)) / range;
            let mf_volume = multiplier * volume;

            self.cmf_accumulator = self.cmf_accumulator
                * ((self.config.cmf_period - 1) as f64 / self.config.cmf_period as f64)
                + mf_volume;
            self.cmf_volume_sum = self.cmf_volume_sum
                * ((self.config.cmf_period - 1) as f64 / self.config.cmf_period as f64)
                + volume;
        }
    }

    /// Update Chaikin Oscillator (A/D Line with fast/slow EMAs)
    fn update_chaikin(&mut self, high: f64, low: f64, close: f64, volume: f64) {
        // Update Accumulation/Distribution Line
        let range = high - low;
        if range > 0.0 {
            let multiplier = ((close - low) - (high - close)) / range;
            self.ad_line += multiplier * volume;
        }

        // Fast EMA of A/D Line
        if let Some(ema) = self.chaikin_fast_ema {
            let k = 2.0 / (self.config.chaikin_fast_period as f64 + 1.0);
            self.chaikin_fast_ema = Some(self.ad_line * k + ema * (1.0 - k));
        } else if self.update_count >= self.config.chaikin_fast_period {
            self.chaikin_fast_ema = Some(self.ad_line);
        }

        // Slow EMA of A/D Line
        if let Some(ema) = self.chaikin_slow_ema {
            let k = 2.0 / (self.config.chaikin_slow_period as f64 + 1.0);
            self.chaikin_slow_ema = Some(self.ad_line * k + ema * (1.0 - k));
        } else if self.update_count >= self.config.chaikin_slow_period {
            self.chaikin_slow_ema = Some(self.ad_line);
        }
    }

    /// Update Donchian Channels (highest high, lowest low over period)
    fn update_donchian(&mut self, high: f64, low: f64) {
        self.donchian_highs.push_back(high);
        self.donchian_lows.push_back(low);

        if self.donchian_highs.len() > self.config.donchian_period {
            self.donchian_highs.pop_front();
            self.donchian_lows.pop_front();
        }
    }

    /// Update On-Balance Volume (OBV)
    fn update_obv(&mut self, price: f64, volume: f64) {
        if let Some(prev_close) = self
            .price_history
            .get(self.price_history.len().saturating_sub(2))
        {
            if price > *prev_close {
                self.obv += volume;
            } else if price < *prev_close {
                self.obv -= volume;
            }
            // If price unchanged, OBV unchanged
        }
    }

    /// Update VWAP (Volume-Weighted Average Price)
    fn update_vwap(&mut self, price: f64, volume: f64) {
        self.vwap_cumulative_pv += price * volume;
        self.vwap_cumulative_volume += volume;
        self.vwap_bar_count += 1;

        // Reset VWAP periodically (e.g., daily)
        if self.vwap_bar_count >= self.config.vwap_reset_period {
            self.vwap_cumulative_pv = price * volume;
            self.vwap_cumulative_volume = volume;
            self.vwap_bar_count = 1;
        }
    }

    /// Update Volume Oscillator (fast/slow EMAs of volume)
    fn update_volume_oscillator(&mut self, volume: f64) {
        // Fast volume EMA
        if let Some(ema) = self.vol_ema_fast {
            let k = 2.0 / (self.config.vol_osc_fast_period as f64 + 1.0);
            self.vol_ema_fast = Some(volume * k + ema * (1.0 - k));
        } else if self.update_count >= self.config.vol_osc_fast_period {
            self.vol_ema_fast = Some(volume);
        }

        // Slow volume EMA
        if let Some(ema) = self.vol_ema_slow {
            let k = 2.0 / (self.config.vol_osc_slow_period as f64 + 1.0);
            self.vol_ema_slow = Some(volume * k + ema * (1.0 - k));
        } else if self.update_count >= self.config.vol_osc_slow_period {
            self.vol_ema_slow = Some(volume);
        }
    }

    // ===== NEW INDICATOR CALCULATION METHODS =====

    /// Calculate Money Flow Index (0-100, overbought >80, oversold <20)
    pub fn calculate_mfi(&self) -> Option<f64> {
        if !self.mfi_state.initialized {
            return None;
        }

        if self.mfi_state.negative_mf == 0.0 {
            return Some(100.0);
        }

        let money_ratio = self.mfi_state.positive_mf / self.mfi_state.negative_mf;
        Some(100.0 - (100.0 / (1.0 + money_ratio)))
    }

    /// Calculate Chaikin Money Flow (-1.0 to 1.0, >0 = buying pressure)
    pub fn calculate_cmf(&self) -> Option<f64> {
        if self.cmf_volume_sum == 0.0 || self.update_count < self.config.cmf_period {
            return None;
        }

        Some(self.cmf_accumulator / self.cmf_volume_sum)
    }

    /// Calculate Chaikin Oscillator (fast EMA - slow EMA of A/D Line)
    pub fn calculate_chaikin_oscillator(&self) -> Option<f64> {
        match (self.chaikin_fast_ema, self.chaikin_slow_ema) {
            (Some(fast), Some(slow)) => Some(fast - slow),
            _ => None,
        }
    }

    /// Calculate Keltner Channels (EMA ± multiplier * ATR)
    pub fn calculate_keltner_channels(&self) -> Option<(f64, f64, f64)> {
        let ema = self.ema_fast_state?;
        let atr = self.atr_state?;

        if self.update_count < self.config.keltner_period {
            return None;
        }

        let upper = ema + self.config.keltner_multiplier * atr;
        let lower = ema - self.config.keltner_multiplier * atr;

        Some((ema, upper, lower))
    }

    /// Calculate Donchian Channels (highest high, lowest low, middle)
    pub fn calculate_donchian_channels(&self) -> Option<(f64, f64, f64)> {
        if self.donchian_highs.len() < self.config.donchian_period {
            return None;
        }

        let highest = self
            .donchian_highs
            .iter()
            .copied()
            .fold(f64::NEG_INFINITY, f64::max);
        let lowest = self
            .donchian_lows
            .iter()
            .copied()
            .fold(f64::INFINITY, f64::min);
        let middle = (highest + lowest) / 2.0;

        Some((middle, highest, lowest))
    }

    /// Get On-Balance Volume
    pub fn calculate_obv(&self) -> f64 {
        self.obv
    }

    /// Calculate VWAP
    pub fn calculate_vwap(&self) -> Option<f64> {
        if self.vwap_cumulative_volume == 0.0 {
            return None;
        }

        Some(self.vwap_cumulative_pv / self.vwap_cumulative_volume)
    }

    /// Calculate Volume Oscillator (%)
    pub fn calculate_volume_oscillator(&self) -> Option<f64> {
        match (self.vol_ema_fast, self.vol_ema_slow) {
            (Some(fast), Some(slow)) if slow != 0.0 => Some(((fast - slow) / slow) * 100.0),
            _ => None,
        }
    }

    /// Calculate current `RSI` (0-100 range)
    pub fn calculate_rsi(&self) -> Option<f64> {
        if !self.rsi_state.initialized {
            return None;
        }

        if self.rsi_state.avg_loss == 0.0 {
            return Some(100.0);
        }

        let rs = self.rsi_state.avg_gain / self.rsi_state.avg_loss;
        Some(100.0 - (100.0 / (1.0 + rs)))
    }

    /// Calculate current `MACD` (difference between fast and slow `EMA`)
    pub fn calculate_macd(&self) -> Option<f64> {
        match (self.ema_fast_state, self.ema_slow_state) {
            (Some(fast), Some(slow)) => Some(fast - slow),
            _ => None,
        }
    }

    /// Calculate `MACD` signal line
    pub fn calculate_macd_signal(&self) -> Option<f64> {
        self.macd_signal_state
    }

    /// Calculate `MACD` histogram
    pub fn calculate_macd_histogram(&self) -> Option<f64> {
        match (self.calculate_macd(), self.macd_signal_state) {
            (Some(macd), Some(signal)) => Some(macd - signal),
            _ => None,
        }
    }

    /// Calculate Bollinger Bands (middle, upper, lower)
    pub fn calculate_bollinger_bands(&self) -> Option<(f64, f64, f64)> {
        if self.price_history.len() < self.config.bollinger_period {
            return None;
        }

        let prices: Vec<f64> = self
            .price_history
            .iter()
            .rev()
            .take(self.config.bollinger_period)
            .copied()
            .collect();

        // Calculate SMA (middle band)
        let sma = prices.iter().sum::<f64>() / prices.len() as f64;

        // Calculate standard deviation
        let variance = prices.iter().map(|p| (p - sma).powi(2)).sum::<f64>() / prices.len() as f64;
        let std_dev = variance.sqrt();

        let upper = sma + self.config.bollinger_std_dev * std_dev;
        let lower = sma - self.config.bollinger_std_dev * std_dev;

        Some((sma, upper, lower))
    }

    /// Get current `ATR` value
    pub fn calculate_atr(&self) -> Option<f64> {
        self.atr_state
    }

    /// Get fast EMA
    pub fn calculate_ema_fast(&self) -> Option<f64> {
        self.ema_fast_state
    }

    /// Get slow EMA
    pub fn calculate_ema_slow(&self) -> Option<f64> {
        self.ema_slow_state
    }

    /// Get current price
    pub fn current_price(&self) -> Option<f64> {
        self.price_history.back().copied()
    }

    /// Check if warmup period is complete
    pub fn is_warmed_up(&self) -> bool {
        self.update_count >= self.config.warmup_period
    }

    /// Get all current indicators as HashMap (36 indicators total)
    pub fn current_indicators(&self) -> std::collections::HashMap<String, f64> {
        let mut indicators = std::collections::HashMap::new();

        // Current price (for fallback when indicators not ready)
        if let Some(price) = self.current_price() {
            indicators.insert("price".to_string(), price);
        }

        // RSI
        if let Some(rsi) = self.calculate_rsi() {
            indicators.insert("rsi".to_string(), rsi);
        }

        // EMA
        if let Some(ema_fast) = self.ema_fast_state {
            indicators.insert("ema_fast".to_string(), ema_fast);
        }
        if let Some(ema_slow) = self.ema_slow_state {
            indicators.insert("ema_slow".to_string(), ema_slow);
        }

        // MACD
        if let Some(macd) = self.calculate_macd() {
            indicators.insert("macd".to_string(), macd);
        }
        if let Some(signal) = self.macd_signal_state {
            indicators.insert("macd_signal".to_string(), signal);
        }
        if let Some(histogram) = self.calculate_macd_histogram() {
            indicators.insert("macd_histogram".to_string(), histogram);
        }

        // Bollinger Bands
        if let Some((middle, upper, lower)) = self.calculate_bollinger_bands() {
            indicators.insert("bollinger_middle".to_string(), middle);
            indicators.insert("bollinger_upper".to_string(), upper);
            indicators.insert("bollinger_lower".to_string(), lower);

            // Bollinger Band width (volatility proxy)
            let width = (upper - lower) / middle;
            indicators.insert("bollinger_width".to_string(), width);
        }

        // ATR
        if let Some(atr) = self.atr_state {
            indicators.insert("atr".to_string(), atr);
        }

        // === NEW MOMENTUM INDICATORS ===
        // Money Flow Index (MFI)
        if let Some(mfi) = self.calculate_mfi() {
            indicators.insert("mfi".to_string(), mfi);
        }

        // Chaikin Money Flow (CMF)
        if let Some(cmf) = self.calculate_cmf() {
            indicators.insert("cmf".to_string(), cmf);
        }

        // Chaikin Oscillator
        if let Some(chaikin) = self.calculate_chaikin_oscillator() {
            indicators.insert("chaikin_oscillator".to_string(), chaikin);
        }

        // === NEW VOLATILITY INDICATORS ===
        // Keltner Channels
        if let Some((middle, upper, lower)) = self.calculate_keltner_channels() {
            indicators.insert("keltner_middle".to_string(), middle);
            indicators.insert("keltner_upper".to_string(), upper);
            indicators.insert("keltner_lower".to_string(), lower);

            // Keltner width
            let width = (upper - lower) / middle;
            indicators.insert("keltner_width".to_string(), width);
        }

        // Donchian Channels
        if let Some((middle, upper, lower)) = self.calculate_donchian_channels() {
            indicators.insert("donchian_middle".to_string(), middle);
            indicators.insert("donchian_upper".to_string(), upper);
            indicators.insert("donchian_lower".to_string(), lower);

            // Donchian width
            let width = (upper - lower) / middle;
            indicators.insert("donchian_width".to_string(), width);
        }

        // === NEW VOLUME INDICATORS ===
        // On-Balance Volume (OBV)
        indicators.insert("obv".to_string(), self.calculate_obv());

        // VWAP
        if let Some(vwap) = self.calculate_vwap() {
            indicators.insert("vwap".to_string(), vwap);

            // VWAP deviation (price distance from VWAP)
            if let Some(price) = self.current_price() {
                let vwap_deviation = (price - vwap) / vwap;
                indicators.insert("vwap_deviation".to_string(), vwap_deviation);
            }
        }

        // Volume Oscillator
        if let Some(vol_osc) = self.calculate_volume_oscillator() {
            indicators.insert("volume_oscillator".to_string(), vol_osc);
        }

        indicators
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
    use super::*;

    #[test]
    fn test_rsi_calculation() {
        let config = IndicatorConfig {
            rsi_period: 14,
            ..Default::default()
        };
        let mut calc = TechnicalIndicatorCalculator::new("TEST".to_string(), config);

        // Feed price data with clear uptrend
        let prices = vec![
            100.0, 101.0, 102.0, 103.0, 104.0, 105.0, 106.0, 107.0, 108.0, 109.0, 110.0, 111.0,
            112.0, 113.0, 114.0,
        ];

        for price in prices {
            calc.update(price, 1000.0, None, None);
        }

        let rsi = calc.calculate_rsi().expect("RSI should be calculated");
        assert!(rsi > 50.0, "Uptrend should have RSI > 50, got {}", rsi);
        assert!(rsi <= 100.0, "RSI should be <= 100, got {}", rsi);
    }

    #[test]
    fn test_ema_calculation() {
        let config = IndicatorConfig {
            ema_fast_period: 5,
            ema_slow_period: 10,
            ..Default::default()
        };
        let mut calc = TechnicalIndicatorCalculator::new("TEST".to_string(), config);

        // Feed constant price - EMA should converge to price
        for _ in 0..20 {
            calc.update(100.0, 1000.0, None, None);
        }

        let ema_fast = calc.calculate_ema_fast().expect("Fast EMA should exist");
        let ema_slow = calc.calculate_ema_slow().expect("Slow EMA should exist");

        assert!(
            (ema_fast - 100.0).abs() < 0.1,
            "Fast EMA should converge to 100"
        );
        assert!(
            (ema_slow - 100.0).abs() < 0.1,
            "Slow EMA should converge to 100"
        );
    }

    #[test]
    fn test_macd_calculation() {
        let config = IndicatorConfig {
            ema_fast_period: 12,
            ema_slow_period: 26,
            macd_signal_period: 9,
            ..Default::default()
        };
        let mut calc = TechnicalIndicatorCalculator::new("TEST".to_string(), config);

        // Feed trending price data
        for i in 0..50 {
            calc.update(100.0 + i as f64, 1000.0, None, None);
        }

        let macd = calc.calculate_macd().expect("MACD should exist");
        let signal = calc.calculate_macd_signal().expect("Signal should exist");
        let histogram = calc
            .calculate_macd_histogram()
            .expect("Histogram should exist");

        assert!(macd > 0.0, "Uptrend should have positive MACD");
        assert!(
            (histogram - (macd - signal)).abs() < 0.001,
            "Histogram should equal MACD - Signal"
        );
    }

    #[test]
    fn test_bollinger_bands() {
        let config = IndicatorConfig {
            bollinger_period: 20,
            bollinger_std_dev: 2.0,
            ..Default::default()
        };
        let mut calc = TechnicalIndicatorCalculator::new("TEST".to_string(), config);

        // Feed price data with volatility
        for i in 0..30 {
            let price = 100.0 + (i as f64 * 0.5).sin() * 5.0;
            calc.update(price, 1000.0, None, None);
        }

        let (middle, upper, lower) = calc
            .calculate_bollinger_bands()
            .expect("Bollinger bands should be calculated");

        assert!(upper > middle, "Upper band should be > middle");
        assert!(middle > lower, "Middle should be > lower band");
        assert!(
            (upper - middle) - (middle - lower) < 0.001,
            "Bands should be symmetric"
        );
    }

    #[test]
    fn test_atr_calculation() {
        let config = IndicatorConfig {
            atr_period: 14,
            ..Default::default()
        };
        let mut calc = TechnicalIndicatorCalculator::new("TEST".to_string(), config);

        // Feed OHLC data with varying ranges
        for i in 0..20 {
            let price = 100.0 + i as f64;
            let high = price + 1.0;
            let low = price - 1.0;
            calc.update(price, 1000.0, Some(high), Some(low));
        }

        let atr = calc.calculate_atr().expect("ATR should be calculated");
        assert!(atr > 0.0, "ATR should be positive");
        assert!(atr < 10.0, "ATR should be reasonable for this data");
    }

    #[test]
    fn test_warmup_period() {
        let config = IndicatorConfig {
            warmup_period: 26,
            ..Default::default()
        };
        let mut calc = TechnicalIndicatorCalculator::new("TEST".to_string(), config);

        assert!(!calc.is_warmed_up(), "Should not be warmed up initially");

        for i in 0..30 {
            calc.update(100.0 + i as f64, 1000.0, None, None);
        }

        assert!(calc.is_warmed_up(), "Should be warmed up after 30 updates");
    }
}
