import datetime
import logging

from apps.TA.storages.data.price import PriceStorage
from apps.backtesting.data_sources import STORAGE_CLASS

from apps.backtesting.utils import datetime_from_timestamp
from apps.doge.doge_TA_actors import CommitteeStorage
from apps.doge.doge_utils import view_keys
from apps.portfolio.services.doge_votes import get_allocations_from_doge
from settings import logger
from settings.redis_db import database


def list_all_committees(ticker='BTC_USDT', exchange='binance'):
    values = database.zrange(f'{ticker}:{exchange}:CommitteeStorage', 0, -1)
    logger.info('Existing committees:')
    for item in values:
        item = item.decode('UTF8').split(':')
        timestamp = CommitteeStorage.timestamp_from_score(item[-1])
        logger.info(f'  ->  at timestamp {datetime_from_timestamp(timestamp)}')

        logger.info(f'This committee produced the following allocations: '
                    f'{get_allocations_from_doge(at_datetime=datetime.datetime.utcfromtimestamp(timestamp))}')


def show_indicator_status(indicator_key='Willr', ticker='BTC_USDT', exchange='binance'):
    indicator_keys = view_keys(f'{ticker}:{exchange}:{indicator_key}*')
    for key in indicator_keys:
        last_entry = database.zrange(key, -1, -1)[0]
        timestamp = CommitteeStorage.timestamp_from_score(last_entry.decode('UTF8').split(':')[-1])
        logger.info(f'For key {key}, last entry is at {datetime_from_timestamp(timestamp)} ({last_entry})')


def duplicate_scores_test():
    from apps.doge.doge_TA_actors import CommitteeVoteStorage
    timestamp = 1548034200
    score = CommitteeVoteStorage.score_from_timestamp(timestamp)
    for i in range(10):
        new_doge_storage = CommitteeVoteStorage(ticker='BTC_USDT',
                                                exchange='binance',
                                                timestamp=timestamp,
                                                periods=12)
        new_doge_storage.value = i
        new_doge_storage.save()
        logging.info(f'Saved an instance of committee vote storage with score {score}')
    logging.info('Querying Redis...')
    results = database.zrangebyscore('BTC_USDT:binance:CommitteeVoteStorage:12', score, score)
    for result in results:
        logging.info(f'     {result}')
    logging.info(f'A total of {len(results)} entries with score {score} found in the database.')


class RedisTests:

    @staticmethod
    def test_ticker_storages(ticker):

        params = dict(
            ticker=ticker,
            exchange="binance",
        )

        from apps.doge.doge_TA_actors import CommitteeVoteStorage
        RedisTests._show_storage_status(CommitteeVoteStorage, params)

        for indicator_type in STORAGE_CLASS:
            RedisTests._show_storage_status(STORAGE_CLASS[indicator_type], params)
        return

    @staticmethod
    def _show_storage_status(storage_class, params):
        sorted_set_key = storage_class.compile_db_key(key=None,
                                                                      key_prefix=f"{params['ticker']}"
                                                                                 f":{params['exchange']}:",
                                                                      key_suffix='*')
        logging.info(f'Processing indicator {sorted_set_key}')
        if len(database.keys(sorted_set_key)) == 0:
            logging.info('   no data.')
        for key in database.keys(sorted_set_key):
            query_response = database.zrange(key, 0, -1)
            if len(query_response) == 0:
                logging.info(f'   {key}: no data')
                continue
            try:
                score_start = query_response[0].decode("utf-8").split(":")[-1]
                score_end = query_response[-1].decode("utf-8").split(":")[-1]
                time_start = datetime_from_timestamp(
                    storage_class.timestamp_from_score(score_start))
                time_end = datetime_from_timestamp(storage_class.timestamp_from_score(score_end))
                num_values = len(query_response)
                logging.info(f'   {key}: {num_values} values, start time = {time_start}, end time = {time_end} (scores {score_start}-{score_end})')
            except Exception as e:
                logging.info(f'Error decoding input: {str(e)}')

    @staticmethod
    def find_gaps(key_pattern, start_timestamp, end_timestamp):

        start_score = PriceStorage.score_from_timestamp(start_timestamp)
        end_score = PriceStorage.score_from_timestamp(end_timestamp)

        gap_dict = {}

        keys = database.keys(key_pattern)
        for key in keys:
            logging.info(f'Processing data for {key}...')
            values = database.zrangebyscore(key, min=start_score, max=end_score)
            gaps = []
            for i, item in enumerate(values):
                if i == len(values) - 1:  # the last element
                    break
                current_score = int(item.decode('UTF8').split(':')[-1])
                next_score = int(values[i + 1].decode('UTF8').split(':')[-1])
                if next_score == current_score:
                    logging.warning(f'     Encountered duplicate scores: {item} and {values[i + 1]}')
                    continue
                if next_score != current_score + 1:
                    gaps.append((current_score, next_score))
            gap_dict[key] = gaps

            logging.info('Found gaps: ')

            for gap in gaps:
                start = datetime_from_timestamp(PriceStorage.timestamp_from_score(gap[0]))
                end = datetime_from_timestamp(PriceStorage.timestamp_from_score(gap[1]))
                logging.info(f'    start: {start}, end: {end}  (scores {gap[0]}-{gap[1]})')

        return gap_dict


if __name__ == '__main__':
    logging.getLogger().setLevel(logging.DEBUG)
    RedisTests.test_ticker_storages('BNB_USDT')