"""
AkDevz AI Market Intelligence — Volatility Analytics Engine
Calculates Bollinger Bands, Keltner Channels, TTM Squeeze, and Historical Volatility.
"""
import numpy as np
from typing import Dict, Any, Tuple
from python.analytics.technical_engine import TechnicalEngine

class VolatilityEngine:
    @staticmethod
    def calculate_bollinger_bands(
        close: np.ndarray,
        period: int = 20,
        num_std: float = 2.0
    ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
        """
        Calculates Middle (SMA), Upper, Lower Bollinger Bands, and Bandwidth.
        """
        n = len(close)
        mid = np.full(n, np.nan, dtype=float)
        upper = np.full(n, np.nan, dtype=float)
        lower = np.full(n, np.nan, dtype=float)
        bandwidth = np.full(n, np.nan, dtype=float)

        if n < period:
            return mid, upper, lower, bandwidth

        for i in range(period - 1, n):
            window = close[i - period + 1 : i + 1]
            m = np.mean(window)
            s = np.std(window, ddof=0)
            mid[i] = m
            upper[i] = m + (num_std * s)
            lower[i] = m - (num_std * s)
            if m > 0:
                bandwidth[i] = ((upper[i] - lower[i]) / m) * 100.0

        return mid, upper, lower, bandwidth

    @staticmethod
    def calculate_keltner_channels(
        high: np.ndarray,
        low: np.ndarray,
        close: np.ndarray,
        ema_period: int = 20,
        atr_period: int = 10,
        multiplier: float = 1.5
    ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
        """Calculates Keltner Channels based on EMA and ATR."""
        ema = TechnicalEngine.calculate_ema(close, ema_period)
        atr = TechnicalEngine.calculate_atr(high, low, close, atr_period)

        upper = ema + (multiplier * atr)
        lower = ema - (multiplier * atr)
        return ema, upper, lower

    @staticmethod
    def detect_volatility_squeeze(
        bb_upper: np.ndarray,
        bb_lower: np.ndarray,
        kc_upper: np.ndarray,
        kc_lower: np.ndarray
    ) -> Dict[str, Any]:
        """
        Detects volatility squeeze (Bollinger Bands contracted entirely inside Keltner Channels).
        """
        if np.isnan(bb_upper[-1]) or np.isnan(kc_upper[-1]):
            return {"squeeze_active": False, "status": "INSUFFICIENT_DATA"}

        is_squeeze = (bb_upper[-1] < kc_upper[-1]) and (bb_lower[-1] > kc_lower[-1])
        was_squeeze = (
            len(bb_upper) > 2
            and (bb_upper[-2] < kc_upper[-2])
            and (bb_lower[-2] > kc_lower[-2])
        )

        squeeze_firing = was_squeeze and not is_squeeze

        return {
            "squeeze_active": bool(is_squeeze),
            "squeeze_fired_recently": bool(squeeze_firing),
            "state": "COMPRESSION" if is_squeeze else ("EXPANSION_TRIGGERED" if squeeze_firing else "NORMAL")
        }

    @staticmethod
    def calculate_historical_volatility(close: np.ndarray, lookback: int = 20) -> float:
        """Annualized Close-to-Close Historical Volatility (HV%)."""
        if len(close) < lookback + 1:
            return 15.0
        log_returns = np.diff(np.log(close[-lookback - 1 :]))
        daily_vol = float(np.std(log_returns, ddof=1))
        annualized_vol = daily_vol * np.sqrt(252) * 100.0
        return float(round(annualized_vol, 2))
