"""
Модуль признаков на основе технических индикаторов.

Функции:
    add_indicator_features — главная функция, добавляет все индикаторы
    add_trend_indicators — трендовые индикаторы (EMA, MACD, ADX)
    add_oscillator_indicators — осцилляторы (RSI, Stochastic, MFI)
    add_volatility_indicators — индикаторы волатильности (BB, ATR)
    add_volume_indicators — объёмные индикаторы (OBV, Volume SMA)

Использует pandas_ta для расчёта индикаторов. pandas_ta автоматически
использует TA-Lib, если он установлен, иначе — собственные реализации.
"""

from typing import Optional

import numpy as np
import pandas as pd
import pandas_ta as ta


def add_trend_indicators(
    df: pd.DataFrame,
) -> pd.DataFrame:
    """
    Добавить трендовые индикаторы.

    Рассчитываются:
        - EMA_9, EMA_21, EMA_50 — экспоненциальные скользящие средние
        - SMA_20, SMA_50, SMA_200 — простые скользящие средние
        - price_to_ema9, price_to_sma50, price_to_sma200 — цена относительно MA
        - MACD (MACD, Signal, Histogram)
        - ADX, PLUS_DI, MINUS_DI

    Args:
        df: DataFrame с колонками High, Low, Close.

    Returns:
        DataFrame с добавленными трендовыми индикаторами.
    """
    df = df.copy()
    c = df['Close']

    # EMA
    df['ema_9'] = ta.ema(c, length=9)
    df['ema_21'] = ta.ema(c, length=21)
    df['ema_50'] = ta.ema(c, length=50)

    # SMA
    df['sma_20'] = ta.sma(c, length=20)
    df['sma_50'] = ta.sma(c, length=50)
    df['sma_200'] = ta.sma(c, length=200)

    # Цена относительно MA
    eps = 1e-8
    df['price_to_ema9'] = c / (df['ema_9'] + eps)
    df['price_to_sma50'] = c / (df['sma_50'] + eps)
    df['price_to_sma200'] = c / (df['sma_200'] + eps)

    # MACD
    macd = ta.macd(c, fast=12, slow=26, signal=9)
    if macd is not None and not macd.empty:
        macd_cols = [col for col in macd.columns if 'MACD' in col.upper()]
        # pandas_ta возвращает колонки вида: MACD_12_26_9, MACDs_12_26_9, MACDh_12_26_9
        if len(macd_cols) >= 3:
            df['macd'] = macd[macd_cols[0]]
            df['macd_signal'] = macd[macd_cols[1]]
            df['macd_hist'] = macd[macd_cols[2]]
        elif len(macd_cols) == 1:
            df['macd'] = macd[macd_cols[0]]
    if 'macd' in df.columns and 'macd_signal' in df.columns:
        df['macd_hist_pct'] = df['macd_hist'] / (df['macd_signal'].abs() + eps)

    # ADX
    adx = ta.adx(df['High'], df['Low'], c, length=14)
    if adx is not None and not adx.empty:
        adx_cols = list(adx.columns)
        if len(adx_cols) >= 3:
            df['adx'] = adx[adx_cols[0]]
            df['plus_di'] = adx[adx_cols[1]]
            df['minus_di'] = adx[adx_cols[2]]

    return df


def add_oscillator_indicators(
    df: pd.DataFrame,
) -> pd.DataFrame:
    """
    Добавить осцилляторы.

    Рассчитываются:
        - RSI (14, 7)
        - Stochastic %K, %D
        - CCI
        - MFI (Money Flow Index)

    Args:
        df: DataFrame с колонками High, Low, Close, Volume.

    Returns:
        DataFrame с добавленными осцилляторами.
    """
    df = df.copy()
    c = df['Close']

    # RSI
    df['rsi_14'] = ta.rsi(c, length=14)
    df['rsi_7'] = ta.rsi(c, length=7)

    # Stochastic
    stoch = ta.stoch(df['High'], df['Low'], c, k=14, d=3)
    if stoch is not None and not stoch.empty:
        stoch_cols = list(stoch.columns)
        if len(stoch_cols) >= 2:
            df['stoch_k'] = stoch[stoch_cols[0]]
            df['stoch_d'] = stoch[stoch_cols[1]]

    # CCI
    cci = ta.cci(df['High'], df['Low'], c, length=20)
    if cci is not None:
        df['cci_20'] = cci

    # MFI (требует Volume)
    if 'Volume' in df.columns:
        df['mfi_14'] = ta.mfi(df['High'], df['Low'], c, df['Volume'], length=14)
        df['mfi_7'] = ta.mfi(df['High'], df['Low'], c, df['Volume'], length=7)

    return df


def add_volatility_indicators(
    df: pd.DataFrame,
) -> pd.DataFrame:
    """
    Добавить индикаторы волатильности.

    Рассчитываются:
        - Bollinger Bands (верхняя, средняя, нижняя, ширина, позиция)
        - ATR (14) и ATR в процентах
        - Keltner Channels (опционально)

    Args:
        df: DataFrame с колонками High, Low, Close.

    Returns:
        DataFrame с добавленными индикаторами волатильности.
    """
    df = df.copy()
    c = df['Close']
    eps = 1e-8

    # Bollinger Bands
    bb = ta.bbands(c, length=20, std=2)
    if bb is not None and not bb.empty:
        bb_cols = list(bb.columns)
        # Ищем колонки: BBL (нижняя), BBM (средняя), BBU (верхняя)
        bbl = [col for col in bb_cols if 'BBL' in col.upper()]
        bbm = [col for col in bb_cols if 'BBM' in col.upper()]
        bbu = [col for col in bb_cols if 'BBU' in col.upper()]
        if bbl and bbm and bbu:
            df['bb_lower'] = bb[bbl[0]]
            df['bb_middle'] = bb[bbm[0]]
            df['bb_upper'] = bb[bbu[0]]
            df['bb_width'] = (df['bb_upper'] - df['bb_lower']) / (df['bb_middle'] + eps)
            df['bb_position'] = (c - df['bb_lower']) / (df['bb_upper'] - df['bb_lower'] + eps)

    # ATR
    df['atr_14'] = ta.atr(df['High'], df['Low'], c, length=14)
    df['atr_pct'] = df['atr_14'] / (c + eps)

    return df


def add_volume_indicators(
    df: pd.DataFrame,
) -> pd.DataFrame:
    """
    Добавить объёмные индикаторы.

    Рассчитываются:
        - OBV (On-Balance Volume)
        - Volume SMA (20)
        - Volume Ratio (текущий объём / средний)
        - Volume Delta (изменение объёма)

    Args:
        df: DataFrame с колонками Close, Volume.

    Returns:
        DataFrame с добавленными объёмными индикаторами.
    """
    df = df.copy()
    v = df['Volume']
    eps = 1e-8

    # OBV
    df['obv'] = ta.obv(df['Close'], v)

    # Volume SMA и Ratio
    df['volume_sma_20'] = ta.sma(v, length=20)
    df['volume_ratio'] = v / (df['volume_sma_20'] + eps)
    df['volume_change'] = v.pct_change(1)
    df['volume_change_5'] = v.pct_change(5)

    return df


def add_indicator_features(
    df: pd.DataFrame,
    include_trend: bool = True,
    include_oscillators: bool = True,
    include_volatility: bool = True,
    include_volume: bool = True,
) -> pd.DataFrame:
    """
    Добавить все группы технических индикаторов.

    Args:
        df: DataFrame с колонками Open, High, Low, Close, Volume.
        include_trend: добавлять трендовые индикаторы (EMA, MACD, ADX).
        include_oscillators: добавлять осцилляторы (RSI, Stochastic, MFI).
        include_volatility: добавлять индикаторы волатильности (BB, ATR).
        include_volume: добавлять объёмные индикаторы (OBV, Volume SMA).

    Returns:
        DataFrame со всеми выбранными группами индикаторов.
    """
    if include_trend:
        df = add_trend_indicators(df)
    if include_oscillators:
        df = add_oscillator_indicators(df)
    if include_volatility:
        df = add_volatility_indicators(df)
    if include_volume:
        df = add_volume_indicators(df)
    return df
