"""
Mapping layer between TA-Lib indicators and TradingView indicators.

This module provides bidirectional conversion between our internal TA-Lib-based
indicator representation and TradingView's indicator system.
"""

from typing import Dict, Any, Optional, Tuple, List
import logging

logger = logging.getLogger(__name__)


# Mapping of TA-Lib indicator names to TradingView indicator names
# Only includes indicators that are present in BOTH systems (inner join)
# Format: {talib_name: tv_name}
TALIB_TO_TV_NAMES = {
    # Overlap Studies (14)
    "SMA": "Moving Average",
    "EMA": "Moving Average Exponential",
    "WMA": "Weighted Moving Average",
    "DEMA": "DEMA",
    "TEMA": "TEMA",
    "TRIMA": "Triangular Moving Average",
    "KAMA": "KAMA",
    "MAMA": "MESA Adaptive Moving Average",
    "T3": "T3",
    "BBANDS": "Bollinger Bands",
    "MIDPOINT": "Midpoint",
    "MIDPRICE": "Midprice",
    "SAR": "Parabolic SAR",
    "HT_TRENDLINE": "Hilbert Transform - Instantaneous Trendline",

    # Momentum Indicators (21)
    "RSI": "Relative Strength Index",
    "MOM": "Momentum",
    "ROC": "Rate of Change",
    "TRIX": "TRIX",
    "CMO": "Chande Momentum Oscillator",
    "DX": "Directional Movement Index",
    "ADX": "Average Directional Movement Index",
    "ADXR": "Average Directional Movement Index Rating",
    "APO": "Absolute Price Oscillator",
    "PPO": "Percentage Price Oscillator",
    "MACD": "MACD",
    "MFI": "Money Flow Index",
    "STOCH": "Stochastic",
    "STOCHF": "Stochastic Fast",
    "STOCHRSI": "Stochastic RSI",
    "WILLR": "Williams %R",
    "CCI": "Commodity Channel Index",
    "AROON": "Aroon",
    "AROONOSC": "Aroon Oscillator",
    "BOP": "Balance Of Power",
    "ULTOSC": "Ultimate Oscillator",

    # Volume Indicators (3)
    "AD": "Chaikin A/D Line",
    "ADOSC": "Chaikin A/D Oscillator",
    "OBV": "On Balance Volume",

    # Volatility Indicators (3)
    "ATR": "Average True Range",
    "NATR": "Normalized Average True Range",
    "TRANGE": "True Range",

    # Price Transform (4)
    "AVGPRICE": "Average Price",
    "MEDPRICE": "Median Price",
    "TYPPRICE": "Typical Price",
    "WCLPRICE": "Weighted Close Price",

    # Cycle Indicators (5)
    "HT_DCPERIOD": "Hilbert Transform - Dominant Cycle Period",
    "HT_DCPHASE": "Hilbert Transform - Dominant Cycle Phase",
    "HT_PHASOR": "Hilbert Transform - Phasor Components",
    "HT_SINE": "Hilbert Transform - SineWave",
    "HT_TRENDMODE": "Hilbert Transform - Trend vs Cycle Mode",

    # Statistic Functions (9)
    "BETA": "Beta",
    "CORREL": "Pearson's Correlation Coefficient",
    "LINEARREG": "Linear Regression",
    "LINEARREG_ANGLE": "Linear Regression Angle",
    "LINEARREG_INTERCEPT": "Linear Regression Intercept",
    "LINEARREG_SLOPE": "Linear Regression Slope",
    "STDDEV": "Standard Deviation",
    "TSF": "Time Series Forecast",
    "VAR": "Variance",
}

# Total: 60 indicators supported in both systems

# Custom indicators (TradingView indicators implemented in our backend)
CUSTOM_TO_TV_NAMES = {
    "VWAP": "VWAP",
    "VWMA": "VWMA",
    "HMA": "Hull Moving Average",
    "SUPERTREND": "SuperTrend",
    "DONCHIAN": "Donchian Channels",
    "KELTNER": "Keltner Channels",
    "CMF": "Chaikin Money Flow",
    "VORTEX": "Vortex Indicator",
    "AO": "Awesome Oscillator",
    "AC": "Accelerator Oscillator",
    "CHOP": "Choppiness Index",
    "MASS": "Mass Index",
}

# Combined mapping (TA-Lib + Custom)
ALL_BACKEND_TO_TV_NAMES = {**TALIB_TO_TV_NAMES, **CUSTOM_TO_TV_NAMES}

# Total: 72 indicators (60 TA-Lib + 12 Custom)

# Reverse mapping
TV_TO_TALIB_NAMES = {v: k for k, v in TALIB_TO_TV_NAMES.items()}
TV_TO_CUSTOM_NAMES = {v: k for k, v in CUSTOM_TO_TV_NAMES.items()}
TV_TO_BACKEND_NAMES = {v: k for k, v in ALL_BACKEND_TO_TV_NAMES.items()}


def get_tv_indicator_name(talib_name: str) -> str:
    """
    Convert TA-Lib indicator name to TradingView indicator name.

    Args:
        talib_name: TA-Lib indicator name (e.g., 'RSI')

    Returns:
        TradingView indicator name
    """
    return TALIB_TO_TV_NAMES.get(talib_name, talib_name)


def get_talib_indicator_name(tv_name: str) -> Optional[str]:
    """
    Convert TradingView indicator name to TA-Lib indicator name.

    Args:
        tv_name: TradingView indicator name

    Returns:
        TA-Lib indicator name or None if not mapped
    """
    return TV_TO_TALIB_NAMES.get(tv_name)


def convert_talib_params_to_tv_inputs(
    talib_name: str,
    talib_params: Dict[str, Any]
) -> Dict[str, Any]:
    """
    Convert TA-Lib parameters to TradingView input format.

    Args:
        talib_name: TA-Lib indicator name
        talib_params: TA-Lib parameter dictionary

    Returns:
        TradingView inputs dictionary
    """
    tv_inputs = {}

    # Common parameter mappings
    param_mapping = {
        "timeperiod": "length",
        "fastperiod": "fastLength",
        "slowperiod": "slowLength",
        "signalperiod": "signalLength",
        "nbdevup": "mult",  # Standard deviations for upper band
        "nbdevdn": "mult",  # Standard deviations for lower band
        "fastlimit": "fastLimit",
        "slowlimit": "slowLimit",
        "acceleration": "start",
        "maximum": "increment",
        "fastk_period": "kPeriod",
        "slowk_period": "kPeriod",
        "slowd_period": "dPeriod",
        "fastd_period": "dPeriod",
        "matype": "maType",
    }

    # Special handling for specific indicators
    if talib_name == "BBANDS":
        # Bollinger Bands
        tv_inputs["length"] = talib_params.get("timeperiod", 20)
        tv_inputs["mult"] = talib_params.get("nbdevup", 2)
        tv_inputs["source"] = "close"
    elif talib_name == "MACD":
        # MACD
        tv_inputs["fastLength"] = talib_params.get("fastperiod", 12)
        tv_inputs["slowLength"] = talib_params.get("slowperiod", 26)
        tv_inputs["signalLength"] = talib_params.get("signalperiod", 9)
        tv_inputs["source"] = "close"
    elif talib_name == "RSI":
        # RSI
        tv_inputs["length"] = talib_params.get("timeperiod", 14)
        tv_inputs["source"] = "close"
    elif talib_name in ["SMA", "EMA", "WMA", "DEMA", "TEMA", "TRIMA"]:
        # Moving averages
        tv_inputs["length"] = talib_params.get("timeperiod", 14)
        tv_inputs["source"] = "close"
    elif talib_name == "STOCH":
        # Stochastic
        tv_inputs["kPeriod"] = talib_params.get("fastk_period", 14)
        tv_inputs["dPeriod"] = talib_params.get("slowd_period", 3)
        tv_inputs["smoothK"] = talib_params.get("slowk_period", 3)
    elif talib_name == "ATR":
        # ATR
        tv_inputs["length"] = talib_params.get("timeperiod", 14)
    elif talib_name == "CCI":
        # CCI
        tv_inputs["length"] = talib_params.get("timeperiod", 20)
    else:
        # Generic parameter conversion
        for talib_param, value in talib_params.items():
            tv_param = param_mapping.get(talib_param, talib_param)
            tv_inputs[tv_param] = value

    logger.debug(f"Converted TA-Lib params for {talib_name}: {talib_params} -> TV inputs: {tv_inputs}")
    return tv_inputs


def convert_tv_inputs_to_talib_params(
    tv_name: str,
    tv_inputs: Dict[str, Any]
) -> Tuple[Optional[str], Dict[str, Any]]:
    """
    Convert TradingView inputs to TA-Lib parameters.

    Args:
        tv_name: TradingView indicator name
        tv_inputs: TradingView inputs dictionary

    Returns:
        Tuple of (talib_name, talib_params)
    """
    talib_name = get_talib_indicator_name(tv_name)
    if not talib_name:
        logger.warning(f"No TA-Lib mapping for TradingView indicator: {tv_name}")
        return None, {}

    talib_params = {}

    # Reverse parameter mappings
    reverse_mapping = {
        "length": "timeperiod",
        "fastLength": "fastperiod",
        "slowLength": "slowperiod",
        "signalLength": "signalperiod",
        "mult": "nbdevup",  # Use same for both up and down
        "fastLimit": "fastlimit",
        "slowLimit": "slowlimit",
        "start": "acceleration",
        "increment": "maximum",
        "kPeriod": "fastk_period",
        "dPeriod": "slowd_period",
        "smoothK": "slowk_period",
        "maType": "matype",
    }

    # Special handling for specific indicators
    if talib_name == "BBANDS":
        # Bollinger Bands
        talib_params["timeperiod"] = tv_inputs.get("length", 20)
        talib_params["nbdevup"] = tv_inputs.get("mult", 2)
        talib_params["nbdevdn"] = tv_inputs.get("mult", 2)
        talib_params["matype"] = 0  # SMA
    elif talib_name == "MACD":
        # MACD
        talib_params["fastperiod"] = tv_inputs.get("fastLength", 12)
        talib_params["slowperiod"] = tv_inputs.get("slowLength", 26)
        talib_params["signalperiod"] = tv_inputs.get("signalLength", 9)
    elif talib_name == "RSI":
        # RSI
        talib_params["timeperiod"] = tv_inputs.get("length", 14)
    elif talib_name in ["SMA", "EMA", "WMA", "DEMA", "TEMA", "TRIMA"]:
        # Moving averages
        talib_params["timeperiod"] = tv_inputs.get("length", 14)
    elif talib_name == "STOCH":
        # Stochastic
        talib_params["fastk_period"] = tv_inputs.get("kPeriod", 14)
        talib_params["slowd_period"] = tv_inputs.get("dPeriod", 3)
        talib_params["slowk_period"] = tv_inputs.get("smoothK", 3)
        talib_params["slowk_matype"] = 0  # SMA
        talib_params["slowd_matype"] = 0  # SMA
    elif talib_name == "ATR":
        # ATR
        talib_params["timeperiod"] = tv_inputs.get("length", 14)
    elif talib_name == "CCI":
        # CCI
        talib_params["timeperiod"] = tv_inputs.get("length", 20)
    else:
        # Generic parameter conversion
        for tv_param, value in tv_inputs.items():
            if tv_param == "source":
                continue  # Skip source parameter
            talib_param = reverse_mapping.get(tv_param, tv_param)
            talib_params[talib_param] = value

    logger.debug(f"Converted TV inputs for {tv_name}: {tv_inputs} -> TA-Lib {talib_name} params: {talib_params}")
    return talib_name, talib_params


def is_indicator_supported(talib_name: str) -> bool:
    """
    Check if a TA-Lib indicator is supported in TradingView.

    Args:
        talib_name: TA-Lib indicator name

    Returns:
        True if supported
    """
    return talib_name in TALIB_TO_TV_NAMES


def get_supported_indicators() -> List[str]:
    """
    Get list of supported TA-Lib indicators.

    Returns:
        List of TA-Lib indicator names
    """
    return list(TALIB_TO_TV_NAMES.keys())


def get_supported_indicator_count() -> int:
    """
    Get count of supported indicators.

    Returns:
        Number of indicators supported in both systems (TA-Lib + Custom)
    """
    return len(ALL_BACKEND_TO_TV_NAMES)


def is_custom_indicator(indicator_name: str) -> bool:
    """
    Check if an indicator is a custom implementation (not TA-Lib).

    Args:
        indicator_name: Indicator name

    Returns:
        True if custom indicator
    """
    return indicator_name in CUSTOM_TO_TV_NAMES


def get_backend_indicator_name(tv_name: str) -> Optional[str]:
    """
    Get backend indicator name from TradingView name (TA-Lib or custom).

    Args:
        tv_name: TradingView indicator name

    Returns:
        Backend indicator name or None if not mapped
    """
    return TV_TO_BACKEND_NAMES.get(tv_name)
