import dataiku
import pandas as pd
import numpy as np
from flask import request
import json

from market_basket_analysis.dku_utils import get_current_project_and_variables


SCOPES_COLORS = [
    "#FF6384", # red
    "#37A2EB", # blue
    "#9967FF", # purple
    "#F28C38", # orange
    "#2A8847", # green
    "#666666", # grey
    "#F6D606" # yellow
]

SORT_ITEMS_ASCENDING = False
DEFAULT_CHART_LABEL = "Transaction items"

project, variables = get_current_project_and_variables()
compute_refined_rules = variables["standard"]["compute_refined_rules_app"]

df_items_kept = dataiku.Dataset("items_kept").get_dataframe()

df_items_kept["item"] = df_items_kept["item"].astype(str).str.replace('"', '')
application_items = list(np.sort(np.unique(df_items_kept["item"])))

if compute_refined_rules:
    association_rules_scope = variables["standard"]["association_rules_scope_app"]
    df_items_kept[association_rules_scope] = df_items_kept[association_rules_scope].astype(str)
    df_items_kept["rule_scope"] = df_items_kept[association_rules_scope].apply(tuple, axis=1).apply(lambda x: "_".join(elem for elem in x))
    rules_scopes = list(np.sort(np.unique(df_items_kept["rule_scope"])))
    
    rules_scopes_items_support = {rule_scope: {} for rule_scope in rules_scopes}
    rules_scopes_dataframes = {}
    
    rules_scopes_items_supports = {rule_scope: {} for rule_scope in rules_scopes}
    rules_scopes_dataframes = {}
    for rule_scope in rules_scopes:
        scope_df = df_items_kept[df_items_kept["rule_scope"]==rule_scope].copy()
        rules_scopes_dataframes[rule_scope] = scope_df
        scope_df_items = list(scope_df["item"])
        scope_df_items_support = list(scope_df["item_support"])
        rules_scopes_items_supports[rule_scope] = {}

        for item, item_support in zip(scope_df_items, scope_df_items_support):
            rules_scopes_items_supports[rule_scope][item] = item_support
    

@app.route('/initialize_frontend_parameters/')
def initialize_frontend_parameters():
    backend_response = {
        "compute_refined_rules": compute_refined_rules,
        "application_items": application_items
    }
    if compute_refined_rules:
        backend_response["rules_scopes"] = rules_scopes
    return json.dumps(backend_response)


@app.route('/refresh_bar_chart_data/', methods=["POST"])
def refresh_bar_chart_data():
    params = request.get_json(force=True)
    print("received params : '{}'".format(params))
    some_items_selected = params["some_items_selected"]
    if some_items_selected:
        selected_items = params["selected_items"]
        print("selected_items : {}".format(selected_items))
    some_scopes_selected = params["some_scopes_selected"]
    n_items_to_analyze = int(params["n_items_to_analyze"])
    filter_on_common_items = params["filter_on_common_items"]
    if filter_on_common_items:
        common_scope_value = int(params["common_scope_value"])
    charts_data = []
    
    if compute_refined_rules:
        use_average_support = True
        if some_scopes_selected:
            selected_rule_scopes = params["selected_rule_scopes"]
            dataframe_of_interest = compute_rules_scope_dataframe(selected_rule_scopes)
            if filter_on_common_items:
                dataframe_of_interest = filter_dataframe_on_common_items(dataframe_of_interest, common_scope_value, selected_rule_scopes)
                
        else:
            dataframe_of_interest = df_items_kept
        
        dataframe_for_filters = dataframe_of_interest
        
        if some_items_selected:
            dataframe_of_interest = filter_dataframe_on_selected_items(dataframe_of_interest, selected_items)
            
        
        items_sorted_by_support, items_average_support = extract_items_support_data(dataframe_of_interest, use_average_support, SORT_ITEMS_ASCENDING)
        available_items, __ = extract_items_support_data(dataframe_for_filters, use_average_support, SORT_ITEMS_ASCENDING)
        items_to_display = items_sorted_by_support[0: n_items_to_analyze]
        
        for index, rule_scope in enumerate(selected_rule_scopes):
            rule_scope_chart_info = {"label": rule_scope, "backgroundColor": SCOPES_COLORS[index], "borderColor": SCOPES_COLORS[index]}
            rule_scope_chart_data = []
            current_scope_items_supports = rules_scopes_items_supports[rule_scope]
            
            for item in items_to_display:
                if item in current_scope_items_supports.keys():
                    item_support = current_scope_items_supports[item]
                    rule_scope_chart_data.append(item_support)                
                else:
                    rule_scope_chart_data.append(0)
            rule_scope_chart_info["data"] = rule_scope_chart_data
            charts_data.append(rule_scope_chart_info)
        
    else:
        use_average_support = False
        if some_items_selected:
            dataframe_of_interest = filter_dataframe_on_selected_items(df_items_kept, selected_items)
        else:
            dataframe_of_interest = df_items_kept
        dataframe_for_filters = df_items_kept
        items_sorted_by_support, items_support = extract_items_support_data(dataframe_of_interest, use_average_support, SORT_ITEMS_ASCENDING)
        available_items, __ = extract_items_support_data(dataframe_for_filters, use_average_support, SORT_ITEMS_ASCENDING)
        items_to_display = items_sorted_by_support[0: n_items_to_analyze]
        rule_scope_chart_info = {"label": DEFAULT_CHART_LABEL, "backgroundColor": SCOPES_COLORS[0], "borderColor": SCOPES_COLORS[0]}
        rule_scope_chart_info["data"] = [items_support[item] for item in items_to_display]
        charts_data.append(rule_scope_chart_info)
    
    matching_items_message = " ({}/{} matching items)".format(len(items_to_display), len(items_sorted_by_support))
    print("matching_items_message : {}".format(matching_items_message))
    backend_response = {
        "charts_data": charts_data,
        "available_items": available_items[0: n_items_to_analyze],
        "items_to_display": items_to_display,
        "matching_items_message": matching_items_message}
    print("backend_response : {}".format(backend_response))
    return json.dumps(backend_response)


def extract_items_support_data(items_support_dataframe, bool_use_average_support, sort_items_ascending):
    items_support_information = {}
    if bool_use_average_support:
        items_support_dataframe = items_support_dataframe[["item", "item_support"]].groupby("item").mean().reset_index()
        items_support_dataframe.rename({"item_support":"item_average_support"}, axis=1, inplace=True)
        items_sort_column = "item_average_support"
    else:
        items_sort_column = "item_support"
    items_support_dataframe = items_support_dataframe.sort_values(by=items_sort_column, ascending=sort_items_ascending)
    items_sorted_by_support = list(items_support_dataframe["item"])
    items_support_values = list(items_support_dataframe[items_sort_column])
    
    items_support_information = {item: support for item, support in zip(items_sorted_by_support, items_support_values)}
        
    return items_sorted_by_support, items_support_information


def compute_rules_scope_dataframe(rules_scope):
    dataframes_to_select = [rules_scopes_dataframes[rule_scope] for rule_scope in rules_scope]
    rules_scope_dataframe = pd.concat(dataframes_to_select)
    return rules_scope_dataframe


def filter_dataframe_on_selected_items(items_support_dataframe, selected_items):
    filtered_dataframe = items_support_dataframe[items_support_dataframe["item"].isin(selected_items)]
    return filtered_dataframe


def filter_dataframe_on_common_items(items_support_dataframe, common_scope_value, selected_rule_scopes):
    n_selected_rule_scopes = len(selected_rule_scopes)
    common_items_threshold = min(common_scope_value, n_selected_rule_scopes)
    common_items_threshold = max(common_items_threshold, 2)
    common_items_df = items_support_dataframe.copy()
    common_items_df["aggregation_counter"] = 1
    common_items_df = common_items_df[["item", "aggregation_counter"]].groupby("item").sum().reset_index()
    common_items_df = common_items_df[common_items_df["aggregation_counter"] >= common_items_threshold]
    common_items_list = list(common_items_df["item"])
    filtered_dataframe = items_support_dataframe[items_support_dataframe["item"].isin(common_items_list)]
    return filtered_dataframe
