import numerapi
from numerapi import utils
from project_tools import project_config, project_utils
from typing import List, Dict
import pandas as pd
import numpy as np

napi = numerapi.NumerAPI()


# def get_round


# depreciated
# def get_model_history(model):
#     res = napi.daily_user_performances(model)
#     res = pd.DataFrame.from_dict(res)
#     res['payoutPending'] = res['payoutPending'].astype(np.float64)
#     res['payoutSettled'] = res['payoutSettled'].astype(np.float64)
#     res['stakeValue'] = res['stakeValue'].astype(np.float64)
#     res['deltaRatio'] = res['payoutPending'] / res['stakeValue']
#     res['realised_pl'] = project_utils.series_reverse_cumsum(res['payoutSettled'])
#     res['floating_pl'] = project_utils.series_reverse_cumsum(res['payoutPending']) - res['realised_pl']
#     res['current_stake'] = res['stakeValue'] - res['floating_pl']
#     rename_dict = {'stakeValue':'floating_stake'}
#     res = res.rename(columns=rename_dict)
#     # res['equity'] = res['stakeValue'] + res['floating_pl']
#     # cols = res.columns.tolist()
#     # res = res[['model'] + cols]
#
#     res['model'] = model
#     cols = ['model', 'date', 'current_stake', 'floating_stake', 'payoutPending', 'floating_pl', 'realised_pl']
#     res = res[cols]
#     return res


def get_portfolio_overview(models, onlylatest=True):
    res_df = []
    for m in models:
        # try:
        print(f'extracting information for model {m}')
        if onlylatest:
            mdf = get_model_history_v3(m).loc[0:0]
        else:
            mdf = get_model_history_v3(m)
        res_df.append(mdf)
        # except:
        #     print(f'no information for model {m} is available')
    if len(res_df)>0:
        res_df = pd.concat(res_df, axis=0)
        # res_df['date'] = res_df['date'].dt.date
        if onlylatest:
            return res_df.sort_values(by='floating_pl', ascending=False).reset_index(drop=True)
        else:
            return res_df.reset_index(drop=True)
    else:
        return None






def get_competitions(tournament=8):
    """Retrieves information about all competitions
    Args:
        tournament (int, optional): ID of the tournament, defaults to 8
            -- DEPRECATED there is only one tournament nowadays
    Returns:
        list of dicts: list of rounds
        Each round's dict contains the following items:
            * datasetId (`str`)
            * number (`int`)
            * openTime (`datetime`)
            * resolveTime (`datetime`)
            * participants (`int`): number of participants
            * prizePoolNmr (`decimal.Decimal`)
            * prizePoolUsd (`decimal.Decimal`)
            * resolvedGeneral (`bool`)
            * resolvedStaking (`bool`)
            * ruleset (`string`)
    Example:
        >>> NumerAPI().get_competitions()
        [
         {'datasetId': '59a70840ca11173c8b2906ac',
          'number': 71,
          'openTime': datetime.datetime(2017, 8, 31, 0, 0),
          'resolveTime': datetime.datetime(2017, 9, 27, 21, 0),
          'participants': 1287,
          'prizePoolNmr': Decimal('0.00'),
          'prizePoolUsd': Decimal('6000.00'),
          'resolvedGeneral': True,
          'resolvedStaking': True,
          'ruleset': 'p_auction'
         },
          ..
        ]
    """
    # self.logger.info("getting rounds...")

    query = '''
        query($tournament: Int!) {
          rounds(tournament: $tournament) {
            number
            resolveTime
            openTime
            resolvedGeneral
            resolvedStaking
          }
        }
    '''
    arguments = {'tournament': tournament}
    result = napi.raw_query(query, arguments)
    rounds = result['data']['rounds']
    # convert datetime strings to datetime.datetime objects
    for r in rounds:
        utils.replace(r, "openTime", utils.parse_datetime_string)
        utils.replace(r, "resolveTime", utils.parse_datetime_string)
        utils.replace(r, "prizePoolNmr", utils.parse_float_string)
        utils.replace(r, "prizePoolUsd", utils.parse_float_string)
    return rounds


def daily_submissions_performances(username: str) -> List[Dict]:
    """Fetch daily performance of a user's submissions.
    Args:
        username (str)
    Returns:
        list of dicts: list of daily submission performance entries
        For each entry in the list, there is a dict with the following
        content:
            * date (`datetime`)
            * correlation (`float`)
            * roundNumber (`int`)
            * mmc (`float`): metamodel contribution
            * fnc (`float`): feature neutral correlation
            * correlationWithMetamodel (`float`)
    Example:
        >>> api = NumerAPI()
        >>> api.daily_user_performances("uuazed")
        [{'roundNumber': 181,
          'correlation': -0.011765912,
          'date': datetime.datetime(2019, 10, 16, 0, 0),
          'mmc': 0.3,
          'fnc': 0.1,
          'correlationWithMetamodel': 0.87},
          ...
        ]
    """
    query = """
              query($username: String!) {
                v2UserProfile(username: $username) {
                  dailySubmissionPerformances {
                    date
                    correlation
                    corrPercentile
                    roundNumber
                    mmc
                    mmcPercentile
                    fnc
                    fncPercentile
                    correlationWithMetamodel
                  }
                }
              }
            """
    arguments = {'username': username}
    data = napi.raw_query(query, arguments)['data']['v2UserProfile']
    performances = data['dailySubmissionPerformances']
    # convert strings to python objects
    for perf in performances:
        utils.replace(perf, "date", utils.parse_datetime_string)
    # remove useless items
    performances = [p for p in performances
                    if any([p['correlation'], p['fnc'], p['mmc']])]
    return performances


def daily_submissions_performances_V3(modelname: str) -> List[Dict]:
    query = """
              query($modelName: String!) {
                v3UserProfile(modelName: $modelName) {
                    roundModelPerformances{
                        roundNumber
                        roundResolveTime
                        corr
                        corrPercentile
                        mmc
                        mmcMultiplier
                        mmcPercentile
                        tc
                        tcPercentile
                        tcMultiplier
                        fncV3
                        fncV3Percentile
                        corrWMetamodel
                        payout
                        roundResolved
                        roundResolveTime
                        corrMultiplier
                        mmcMultiplier
                        selectedStakeValue
                    }
                    stakeValue
                    nmrStaked
                }
              }
            """
    arguments = {'modelName': modelname}
    data = napi.raw_query(query, arguments)['data']['v3UserProfile']
    performances = data['roundModelPerformances']
    # convert strings to python objects
    for perf in performances:
        utils.replace(perf, "date", utils.parse_datetime_string)
    # remove useless items
    performances = [p for p in performances
                    if any([p['corr'], p['tc'], p['mmc']])]
    return performances


def get_lb_models(limit=20000, offset=0):
    query = """
           query($limit: Int, $offset: Int){
               v2Leaderboard(limit:$limit, offset:$offset){
                   username
               }           
           }
           """
    arguments = {'limit':limit, 'offset':offset}
    data = napi.raw_query(query, arguments)['data']['v2Leaderboard']
    model_list = [i['username'] for i in data]
    return model_list



def get_round_model_performance(roundNumber: int, model: str):
    query = """
              query($roundNumber: Int!, $username: String!) {
                  roundSubmissionPerformance(roundNumber: $roundNumber, username: $username) {
                      corrMultiplier
                      mmcMultiplier                      
                      roundDailyPerformances{
                          correlation
                          mmc
                          corrPercentile
                          mmcPercentile
                          payoutPending
                       }
                       selectedStakeValue
                   }
              }
            """
    arguments = {'roundNumber': roundNumber,'username': model}
    data = napi.raw_query(query, arguments)['data']['roundSubmissionPerformance']
    latest_performance = data['roundDailyPerformances'][-1] #[-1] ### issue with order
    res = {}
    res['model'] = model
    res['roundNumber'] = roundNumber
    res['corrMultiplier'] = data['corrMultiplier']
    res['mmcMultiplier'] = data['mmcMultiplier']
    res['selectedStakeValue'] = data['selectedStakeValue']
    for key in latest_performance.keys():
        res[key] = latest_performance[key]
    return res




def get_user_profile(username: str) -> List[Dict]:
    """Fetch daily performance of a user's submissions.
    Args:
        username (str)
    Returns:
        list of dicts: list of daily submission performance entries
        For each entry in the list, there is a dict with the following
        content:
            * date (`datetime`)
            * correlation (`float`)
            * roundNumber (`int`)
            * mmc (`float`): metamodel contribution
            * fnc (`float`): feature neutral correlation
            * correlationWithMetamodel (`float`)
    Example:
        >>> api = NumerAPI()
        >>> api.daily_user_performances("uuazed")
        [{'roundNumber': 181,
          'correlation': -0.011765912,
          'date': datetime.datetime(2019, 10, 16, 0, 0),
          'mmc': 0.3,
          'fnc': 0.1,
          'correlationWithMetamodel': 0.87},
          ...
        ]
    """
    query = """
              query($username: String!) {
                v2UserProfile(username: $username) {
                  dailySubmissionPerformances {
                    date
                    correlation
                    corrPercentile
                    roundNumber
                    mmc
                    mmcPercentile
                    fnc
                    fncPercentile
                    correlationWithMetamodel
                  }
                }
              }
            """
    arguments = {'username': username}
    data = napi.raw_query(query, arguments)['data']#['v2UserProfile']
    # performances = data['dailySubmissionPerformances']
    # # convert strings to python objects
    # for perf in performances:
    #     utils.replace(perf, "date", utils.parse_datetime_string)
    # # remove useless items
    # performances = [p for p in performances
    #                 if any([p['correlation'], p['fnc'], p['mmc']])]
    return data


def download_dataset(filename: str, dest_path: str = None,
                     round_num: int = None) -> None:
    """ Download specified file for the current active round.

    Args:
        filename (str): file to be downloaded
        dest_path (str, optional): complate path where the file should be
            stored, defaults to the same name as the source file
        round_num (int, optional): tournament round you are interested in.
            defaults to the current round
        tournament (int, optional): ID of the tournament, defaults to 8

    Example:
        >>> filenames = NumerAPI().list_datasets()
        >>> NumerAPI().download_dataset(filenames[0]}")
    """
    if dest_path is None:
        dest_path = filename

    query = """
    query ($filename: String!
           $round: Int) {
        dataset(filename: $filename
                round: $round)
    }
    """
    args = {'filename': filename, "round": round_num}

    dataset_url = napi.raw_query(query, args)['data']['dataset']
    utils.download_file(dataset_url, dest_path, show_progress_bars=True)
    
    
    
# function using V3UserProfile

def model_payout_history(model):
    napi = numerapi.NumerAPI()
    query = """
              query($model: String!) {
                  v3UserProfile(modelName: $model) {
                        roundModelPerformances{
                            payout
                            roundNumber
                            roundResolved
                            roundResolveTime
                            corrMultiplier
                            mmcMultiplier
                            selectedStakeValue
                        }
                        stakeValue
                        nmrStaked
                   }
              }
            """
    arguments = {'model': model}
    payout_info = napi.raw_query(query, arguments)['data']['v3UserProfile']['roundModelPerformances']
    payout_info = pd.DataFrame.from_dict(payout_info)
    payout_info = payout_info[~pd.isnull(payout_info['payout'])].reset_index(drop=True)
    return payout_info


def get_model_history_v3(model):
    res = model_payout_history(model)
    res = pd.DataFrame.from_dict(res)
    res['payout'] = res['payout'].astype(np.float64)
    res['current_stake'] = res['selectedStakeValue'].astype(np.float64)
    res['payout_cumsum'] = project_utils.series_reverse_cumsum(res['payout'])
    res['date'] = pd.to_datetime(res['roundResolveTime']).dt.date

    res['realised_pl'] = res['payout_cumsum']
    latest_realised_pl = res[res['roundResolved'] == True]['payout_cumsum'].values[0]
    res.loc[res['roundResolved'] == False, 'realised_pl'] = latest_realised_pl

    res['floating_pl'] = 0
    payoutPending_values = res[res['roundResolved'] == False]['payout'].values
    payoutPending_cumsum = payoutPending_values[::-1].cumsum()[::-1]
    res.loc[res['roundResolved'] == False, 'floating_pl'] = payoutPending_cumsum

    res['model'] = model
    #     res['floating_pl'] = res['current_stake'] + res['payoutPending']
    res['floating_stake'] = res['current_stake'] + res['floating_pl']
    cols = ['model', 'date', 'current_stake', 'floating_stake', 'payout', 'floating_pl', 'realised_pl', 'roundResolved',
            'roundNumber']
    res = res[cols]
    return res