import random
from functools import reduce

import pandas as pd
from fuzzywuzzy import process
from fuzzywuzzy import fuzz

import utils.gennumbers as gennumbers


# ---------------------------------------------------------------------

def fuzzy_match_ents(ents, choices, limit=2, thresh=80):
    fuzz_matches_out = []
    for ent in ents:
        top_matches = process.extract(
            ent,
            set(choices),
            limit=limit,
            scorer=fuzz.partial_ratio)
        for match, score in top_matches:
            if score >= thresh:
                fuzz_matches_out.append(match)
    return fuzz_matches_out


def gen_rank_translation():
    # generate valid chart ranks in number (1) and word (one) forms
    numranks = {str(x): x for x in range(1, 101)}
    numranks_str = {x: i + 1 for i, x in enumerate(gennumbers.word_nums_100())}
    #
    # generate ordinal ranks in number (1st) and word forms (first)
    ordranks = {x: i + 1 for i, x in enumerate(gennumbers.ordinal_nums_100())}
    ordranks_str = [
        'first', 'second', 'third', 'fourth', 'fifth',
        'sixth', 'seventh', 'eighth', 'ninth', 'tenth',
        'eleventh', 'twelfth', 'thirteenth', 'fourteenth', 'fifteenth'
    ]
    ordranks_str = {x: i + 1 for i, x in enumerate(ordranks_str)}
    #
    word2int = {}
    for d in (numranks, numranks_str, ordranks, ordranks_str):
        word2int.update(d)
    #
    return word2int


rank_dict = gen_rank_translation()


def respond(message, interpreter, app_data_path):
    app_data = pd.read_csv(app_data_path)

    parsed = interpreter.parse(message)

    generic_app_response = False

    if parsed['intent']['name'] == 'greet':
        greetings = ['hey there', 'hi', 'howdy', 'hello']
        suggestions = [
            '"what rank is snapchat?"',
            '"what is the number 1 free app?"',
            '"what are the top ranked productivity apps?"',
            '"what games are popular?"'
        ]
        greeting = random.choice(greetings)
        suggestion = random.choice(suggestions)

        response = ['{}, try asking me a question about apps like {}'.format(greeting, suggestion)]

    elif parsed['intent']['name'] == 'goodbye':
        closings = ['cya!', 'goodbye', 'have a good day!', 'peace out']

        response = [random.choice(closings)]

    elif parsed['intent']['name'] == 'app_rank_search':
        if len(parsed['entities']) > 0:
            ents_df = pd.DataFrame(parsed['entities'])

            chart_rows = ents_df['entity'] == 'chart'
            genre_rows = ents_df['entity'] == 'genre'
            app_rows = ents_df['entity'] == 'app'
            rank_rows = ents_df['entity'].isin(['ordrank', 'numrank'])

            # convert ranks to ints
            chart_matches = list(ents_df.loc[chart_rows, 'value'])
            genre_matches = list(ents_df.loc[genre_rows, 'value'])
            app_matches = list(ents_df.loc[app_rows, 'value'])

            chart_filters = fuzzy_match_ents(chart_matches, app_data['chart'])
            genre_filters = fuzzy_match_ents(genre_matches, app_data['genre'])
            app_filters = fuzzy_match_ents(app_matches, app_data['app'])

            rank_filters = list(ents_df.loc[rank_rows, 'value'].apply(lambda x: rank_dict[x]))

            df_list = []
            if len(chart_filters) > 0:
                chart_filtered = app_data.loc[app_data['chart'].isin(chart_filters)]
                df_list.append(chart_filtered)

            if len(genre_filters) > 0:
                genre_filtered = app_data.loc[app_data['genre'].isin(genre_filters)]
                df_list.append(genre_filtered)

            if len(app_filters) > 0:
                app_filtered = app_data.loc[app_data['app'].isin(app_filters)]
                df_list.append(app_filtered)

            if len(rank_filters) > 0:
                rank_filtered = app_data.loc[app_data['rank'].isin(rank_filters)]
                df_list.append(rank_filtered)

            if len(df_list) > 1:
                filt_inds = reduce(lambda x, y: set(x.index).intersection(set(y.index)), df_list)
            elif len(df_list) == 1:
                filt_inds = set(df_list[0].index)
            else:
                filt_inds = None

            if filt_inds is not None:
                query_result = app_data.iloc[list(filt_inds),]
                query_result = query_result.sort_values(by=['rank']).reset_index(drop=True)

                if query_result.shape[0] > 0:
                    response_statements = []

                    responses = [
                        '{app} is a {genre} app ranked {rank} on the {chart} chart',
                        'a popular {genre} app is {app}; it\'s ranked {rank} on the {chart} chart',
                        'number {rank} on the {chart} chart is the {genre} app {app}',
                        'if you\'re interested in {genre}, then check out {app}. it\'s ranked {rank} among {chart}'
                    ]
                    response_choice = random.choice(responses)

                    for i, row in query_result.iterrows():
                        response_i = response_choice
                        response_i = response_i.format(
                            app=row['app'],
                            rank=row['rank'],
                            chart=row['chart'],
                            genre=row['genre'])
                        response_statements.append(response_i)
                        if i >= 2:
                            break
                    response = response_statements
                else:
                    response = ["i couldn't find anything related to your app search :("]
            else:
                response = ["i couldn't find anything related to your app search :("]
        else:
            query_result = app_data.sort_values(by=['rank']).reset_index(drop=True)

            response_statements = []

            for i, row in query_result.iterrows():
                response_i = '- {app} is a {genre} app ranked {rank} on the {chart} chart'
                response_i = response_i.format(
                    app=row['app'],
                    rank=row['rank'],
                    chart=row['chart'],
                    genre=row['genre'])
                response_statements.append(response_i)
                if i >= 2:
                    break
            response = response_statements
            generic_app_response = True
    else:
        apologies = [
            "i didn't understand that",
            "i'm not the smartest bot",
            "sorry, i didn't catch that"
        ]
        suggestions = [
            '"what rank is facebook?"',
            '"what is the number 1 top grossing app?"',
            '"what are the top ranked medical apps?"',
            '"what apps are popular?"'
        ]
        apology = random.choice(apologies)
        suggestion = random.choice(suggestions)

        response = ['{}, try asking me something like {}'.format(apology, suggestion)]

    return response, generic_app_response