import pandas as pd
import datetime
import numpy as np

import market_trade.core.CoreTradeMath
from tqdm import tqdm

from market_trade.core.indicators_v2 import IndicatorsAggregator, ind_BB


class CoreSignalTrade:
    """Base class for trading signals.

    Provides foundation for generating trading signals based on technical indicators.
    """

    def __init__(self, name: str, req: dict, data_type: str):
        """Initialize signal generator.

        Args:
            name: Signal identifier.
            req: Configuration dictionary containing params and indicators.
            data_type: Type of data to process (e.g., 'ohlc').
        """
        self.name = name
        self.aggregate_indicators = self.create_indicators_instance(req)
        self.params = req['params']
        self.data_type = data_type

    def create_indicators_instance(self, req: dict) -> IndicatorsAggregator:
        """Create indicators aggregator from configuration.

        Args:
            req: Request dictionary containing indicators configuration.

        Returns:
            IndicatorsAggregator instance.
        """
        return IndicatorsAggregator(req['indicators'])

    def get_indicator_answer(self, data_dict: dict) -> dict:
        """Get answers from all indicators.

        Args:
            data_dict: Dictionary mapping indicator names to data.

        Returns:
            Dictionary of indicator results.
        """
        return self.aggregate_indicators.get_answer(data_dict)

    def get_answer(self, data: pd.DataFrame, ind_data_dict: dict) -> dict:
        """Get signal answer from data and indicator results.

        Args:
            data: Market data DataFrame.
            ind_data_dict: Dictionary of indicator data.

        Returns:
            Signal answer (direction).
        """
        return self.get_signal_answer(data, self.get_indicator_answer(ind_data_dict))


class sig_BB(CoreSignalTrade):
    """Bollinger Bands signal generator.

    Generates trading signals based on Bollinger Bands indicator:
    - 'up' when price is below lower band
    - 'down' when price is above upper band
    - 'none' when price is within bands

    Required indicator keys:
        ind_BB: Bollinger Bands indicator
    """

    def __init__(self, name: str, req: dict):
        """Initialize Bollinger Bands signal.

        Args:
            name: Signal identifier.
            req: Configuration dictionary.
        """
        super().__init__(name, req, 'ohlc')

    def get_signal_answer(self, data: pd.DataFrame, ind_ans_dict: dict) -> str:
        """Calculate signal from Bollinger Bands.

        Args:
            data: Market data DataFrame.
            ind_ans_dict: Dictionary containing indicator results.

        Returns:
            Signal direction: 'up', 'down', or 'none'.
        """
        last_value = data[self.params['source']].to_list()[-1]
        if last_value > ind_ans_dict['ind_BB']['pSTD'][-1]:
            ans = 'down'
        elif last_value < ind_ans_dict['ind_BB']['mSTD'][-1]:
            ans = 'up'
        else:
            ans = 'none'

        return ans


class SignalsAggregator:
    """Aggregates and manages multiple signal generators.

    Example usage:
        sig_config = {
            'sig_BB': {
                'className': sig_BB,
                'params': {'source': 'close', 'target': 'close'},
                'indicators': {
                    'ind_BB': {
                        'className': ind_BB,
                        'params': {'MeanType': 'SMA', 'window': 15, 'valueType': 'close', 'kDev': 2.5}
                    }
                }
            }
        }

        sig_data = {
            'sig_BB': {
                'signalData': df_candle[990:1000],
                'indicatorData': {'ind_BB': df_candle[:1000]}
            }
        }

        aggregator = SignalsAggregator(sig_config)
        results = aggregator.get_answer(sig_data)
    """

    def __init__(self, req: dict):
        """Initialize signals aggregator.

        Args:
            req: Dictionary mapping signal names to configurations.
        """
        self.signals = self.create_signals_instance(req)

    def create_signals_instance(self, signals_dict: dict) -> dict:
        """Create instances of all configured signals.

        Args:
            signals_dict: Dictionary of signal configurations.

        Returns:
            Dictionary of signal instances.
        """
        ans = {}
        for i in signals_dict.keys():
            ans[i] = signals_dict[i]['className'](name=i, req=signals_dict[i])
        return ans

    def get_answer(self, data_dict: dict) -> dict:
        """Calculate answers from all signals.

        Args:
            data_dict: Dictionary mapping signal names to their data.
                Each entry should contain 'signalData' and 'indicatorData'.

        Returns:
            Dictionary of signal results.
        """
        ans = {}
        for i in data_dict.keys():
            ans[i] = self.signals[i].get_answer(
                data=data_dict[i]['signalData'],
                ind_data_dict=data_dict[i]['indicatorData']
            )
        return ans
