# -*- coding: utf-8 -*-

""" This module offers util functions to be called and used
    in other modules
"""

from datetime import datetime
import os
import json
import pickle
import string
import random
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
from sklearn import tree


def id_generator(size=6, chars=string.ascii_lowercase + string.digits):
    """GENERATE A RANDOM STRING TO BE USED AS AN ID

    Args:
        size (int, optional): size of the string. Defaults to 6.
        chars (str, optional): charachters to be used to generate the string.
                            Defaults to string.ascii_lowercase+string.digits.

    Returns:
        [str]: a random chain of charachters
    """
    return "".join(random.choice(chars) for _ in range(size))


def save_model(path, model):
    """SAVE MODEL INTO PICKLE FILE

    Args:
        path (str): path where to save the model
        model (binary): the model to be saved
    """

    with open(path, "wb") as file:
        pickle.dump(model, file)


def update_history(models_hist_path, model_id, model_name, model, params):
    """SAVE METADATA RELATED TO THE TRAINED MODEL INTO THE HISTORY FILE

    Args:
        models_hist_path (str): path to the history file
        model_id (str): unique id of the model
        model_name (str): model name = "model_"+model_id+".pkl"
        model (binary): binary file of the model
        params (dict): dictionnary containing the hyper-parameters
                        used to fit the model
    """

    model_metadata = dict()
    model_metadata["trained"] = str(datetime.now())
    model_metadata["model_type"] = type(model).__name__
    model_metadata["model_id"] = model_id
    model_metadata["params"] = params
    print(model_metadata)

    with open(models_hist_path, "r+") as outfile:
        try:
            hist = json.load(outfile)
            hist[model_name] = model_metadata
            outfile.seek(0)
            json.dump(hist, outfile, indent=4)
        except json.decoder.JSONDecodeError:
            json.dump({model_name: model_metadata}, outfile, indent=4)


def update_history_add_eval(
    models_hist_path, model_id=None, model_name=None, metrics=None
):
    """ADD EVALUATION METRICS THE HISTORY FILE FOR THE SPECIFIED MODEL

    Args:
        models_hist_path (str): path to the history file
        model_id (str, optional): the id of the model. Defaults to None.
        model_name (str, optional): the name of the model. Defaults to None.
        metrics (dict, optional): a dictionnary containing metadata related
                                    to the model evaluation. Defaults to None.
    """

    assert (
        model_id is not None or model_name is not None
    ), "At least the model id or name must be given"
    assert models_hist_path is not None, "You must specify the path to the history file"

    if not model_name:
        model_name = "model_" + model_id + ".pkl"

    eval_metadata = dict()
    eval_metadata["datetime"] = str(datetime.now())
    eval_metadata["metrics"] = metrics

    with open(models_hist_path, "r+") as outfile:
        try:
            hist = json.load(outfile)
            hist[model_name]["evaluation"] = eval_metadata
            outfile.seek(0)
            json.dump(hist, outfile, indent=4)
        except json.decoder.JSONDecodeError:
            print("cannot save evaluation metadata")


def generate_features_importance_plot(model, features, model_id):
    """GENERATES A PLOT DESCRIBING FEATURES IMPORTANCE FOR THE MODEL
    TO MAKE THE PREDICTION.

    Args:
        model (tree-based model): a tree based model (decision tree, random forest ...)
        features (pandas dataframe): a table of the features on which we trained the model
        model_id (str): the unique id of the model
    """
    mean_importances = model.feature_importances_
    importances_indices = np.argsort(mean_importances)[::-1]
    ordered_columns = [features.columns[i] for i in importances_indices]
    importances = pd.DataFrame(
        [tree.feature_importances_ for tree in model.estimators_],
        columns=features.columns,
    )
    importances = importances[ordered_columns]
    _, ax = plt.subplots(figsize=(12, 8))
    sns.boxplot(x="variable", y="value", ax=ax, data=pd.melt(importances))
    figure = ax.get_figure()
    figure.savefig(
        "models/models-training/run_" + model_id + "/features_importance.png"
    )


def plot_trees(rf, feature_names, target_names, model_id):
    """GENERATES A PLOT THAT SHOWS THE DECISION MAKING OF THE TREES

    Args:
        rf (model): a tree based model (random forest ...)
        feature_names (list): names of the columns of the training set
        target_names (str): name of the target columns
        model_id (str): unique id of the model
    """
    fn = feature_names
    cn = target_names
    fig, axes = plt.subplots(nrows=1, ncols=5, figsize=(10, 2), dpi=900)
    for index in range(0, 5):
        tree.plot_tree(
            rf.estimators_[index],
            feature_names=fn,
            class_names=cn,
            filled=True,
            ax=axes[index],
        )

        axes[index].set_title("Estimator: " + str(index), fontsize=11)
    fig.savefig("models/models-training/run_" + model_id + "/Trees.png")


def get_id_list(N=6):
    print (os.getcwd())
    print([x[0] for x in os.walk("../../models/models-training")])
    return [x[0][-N:] for x in os.walk("../../models/models-training")][1:]
[((122, 18, 125, 5), 'pandas.DataFrame', 'pd.DataFrame', (), '', True, 'import pandas as pd\n'), ((127, 12, 127, 41), 'matplotlib.pyplot.subplots', 'plt.subplots', (), '', True, 'from matplotlib import pyplot as plt\n'), ((146, 16, 146, 72), 'matplotlib.pyplot.subplots', 'plt.subplots', (), '', True, 'from matplotlib import pyplot as plt\n'), ((43, 8, 43, 32), 'pickle.dump', 'pickle.dump', ({(43, 20, 43, 25): 'model', (43, 27, 43, 31): 'file'}, {}), '(model, file)', False, 'import pickle\n'), ((59, 36, 59, 50), 'datetime.datetime.now', 'datetime.now', ({}, {}), '()', False, 'from datetime import datetime\n'), ((97, 36, 97, 50), 'datetime.datetime.now', 'datetime.now', ({}, {}), '()', False, 'from datetime import datetime\n'), ((120, 26, 120, 54), 'numpy.argsort', 'np.argsort', ({(120, 37, 120, 53): 'mean_importances'}, {}), '(mean_importances)', True, 'import numpy as np\n'), ((148, 8, 154, 9), 'sklearn.tree.plot_tree', 'tree.plot_tree', (), '', False, 'from sklearn import tree\n'), ((161, 11, 161, 22), 'os.getcwd', 'os.getcwd', ({}, {}), '()', False, 'import os\n'), ((31, 19, 31, 39), 'random.choice', 'random.choice', ({(31, 33, 31, 38): 'chars'}, {}), '(chars)', False, 'import random\n'), ((67, 19, 67, 37), 'json.load', 'json.load', ({(67, 29, 67, 36): 'outfile'}, {}), '(outfile)', False, 'import json\n'), ((70, 12, 70, 46), 'json.dump', 'json.dump', (), '', False, 'import json\n'), ((102, 19, 102, 37), 'json.load', 'json.load', ({(102, 29, 102, 36): 'outfile'}, {}), '(outfile)', False, 'import json\n'), ((105, 12, 105, 46), 'json.dump', 'json.dump', (), '', False, 'import json\n'), ((128, 53, 128, 73), 'pandas.melt', 'pd.melt', ({(128, 61, 128, 72): 'importances'}, {}), '(importances)', True, 'import pandas as pd\n'), ((72, 12, 72, 70), 'json.dump', 'json.dump', (), '', False, 'import json\n'), ((162, 25, 162, 64), 'os.walk', 'os.walk', ({(162, 33, 162, 63): '"""../../models/models-training"""'}, {}), "('../../models/models-training')", False, 'import os\n'), ((163, 31, 163, 70), 'os.walk', 'os.walk', ({(163, 39, 163, 69): '"""../../models/models-training"""'}, {}), "('../../models/models-training')", False, 'import os\n')]