import numpy as np
import pandas as pd
from IPython.core.interactiveshell import InteractiveShell
from IPython.display import HTML, display
from sklearn.metrics import make_scorer, mean_squared_error
from sklearn.model_selection import cross_val_score

from appendix_figures import interactive_results


def replace_neg_pred(y):
    """ Replace negative values of regression models predictions by 0
    :param y: predicted values
    :returns: transformed predicted values
    :rtype: np.array
    """

    return np.array(list(map(lambda v: 0 if v < 0 else v, y)))


def rmse(y_true, y_pred):
    return np.sqrt(mean_squared_error(y_true * 100, y_pred * 100))


def fit_predict_evaluate_plot(model, X_train, X_test,
                              y_train, y_test, y_train_t, log=False, cv=7):
    model.fit(X_train, y_train_t)
    if log:
        y_train_pred = np.expm1(replace_neg_pred(model.predict(X_train)))
        y_test_pred = np.expm1(replace_neg_pred(model.predict(X_test)))
    else:
        y_train_pred = replace_neg_pred(model.predict(X_train)) * 100
        y_test_pred = replace_neg_pred(model.predict(X_test)) * 100

    res = np.array([[np.sqrt(mean_squared_error(y_train, y_train_pred))],
                    [np.sqrt(mean_squared_error(y_test, y_test_pred))]])

    display(
        HTML(
            pd.DataFrame(
                res,
                index=[
                    "Train Set",
                    "Test Set"],
                columns=["rmse"]).to_html()))

    # Cross Validation
    rmse_scores = np.array(
        cross_val_score(model, X_train, y_train_t, scoring=make_scorer(rmse), cv=cv, n_jobs=-1))
    mean_rmse_scores = rmse_scores.mean()
    std_rmse_scores = rmse_scores.std()

    res_cv = [mean_rmse_scores, std_rmse_scores, *rmse_scores]

    columns_name = ['mean_cv', 'std_cv'] + \
        ['cv_{}'.format(i) for i in range(1, cv + 1)]
    display(
        HTML(
            pd.DataFrame(
                np.array(res_cv).reshape(
                    1,
                    9),
                index=[
                    'CV={}'.format(cv)],
                columns=columns_name).to_html()))

    interactive_results(y_train, y_train_pred, y_test, y_test_pred)


def missing_ratio(data):
    """ Returns a new dataframe with all features as index and their
    corresponding ratio of missing values
    :param data: dataframe
    :returns: dataframe of missing values ratio for each feature
    :rtype: pandas.DataFrame
    """

    data_na = (
        (data == 0).sum() *
        100 /
        len(data)).sort_values(
        ascending=False)
    missing_data = pd.DataFrame({'Missing Ratio': data_na})

    return missing_data
