import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.axes import Axes
from statsmodels.stats.multicomp import pairwise_tukeyhsd

def exclude_outliers(df, column_name, group: str = None, threshold=1.5):
    """
    Exclude outliers from a DataFrame column using the IQR method.

    Parameters:
    - df: pandas DataFrame
    - column_name: string, name of the column to process
    - threshold: float, multiplier for IQR (default 1.5)

    Returns:
    - DataFrame with outliers removed
    """
    filtered_index = []

    if group == None:
        df_local = df.copy()
        Q1 = df_local[column_name].quantile(0.25)
        Q3 = df_local[column_name].quantile(0.75)
        IQR = Q3 - Q1

        # Define bounds
        lower_bound = Q1 - threshold * IQR
        upper_bound = Q3 + threshold * IQR

        # Filter the DataFrame
        mask = (df_local[column_name] >= lower_bound) & (df_local[column_name] <= upper_bound)
        return df_local[mask]

    for each_category in df[group].unique():

    # Calculate Q1, Q3 and IQR

        df_local =  df[df[group]==each_category]
        Q1 = df_local[column_name].quantile(0.25)
        Q3 = df_local[column_name].quantile(0.75)
        IQR = Q3 - Q1

        # Define bounds
        lower_bound = Q1 - threshold * IQR
        upper_bound = Q3 + threshold * IQR

        # Filter the DataFrame
        mask = (df_local[column_name] >= lower_bound) & (df_local[column_name] <= upper_bound)
        filtered_df : pd.DataFrame = df_local[mask]
        index_good = filtered_df.index.tolist()
        filtered_index+=index_good
    return df.loc[filtered_index]

def perform_posthoc(df : pd.DataFrame, column_name: str, group: str, alpha=0.05):
    '''
    Faz um teste de tukey, em um propriedade, separando entre grupos
    '''
    families = df.groupby(by=[group])[column_name].apply(list)
    # shapiro = [shapiro(g).pvalue for g in families]
    result = pairwise_tukeyhsd(endog=df[column_name],groups=df[group], alpha=alpha)

    print(result)

    # Relative initial value
    comparative_values = df[[column_name,group]].groupby(by=[group]).mean()
    # comparative_val = comparative_values[column_name].loc[result.groupsunique[0]]
    # relative_diffs = []
    # for index, mean_diff in enumerate(result.meandiffs):
    #     if result.reject[index]:
    #         relative_diffs.append(mean_diff/comparative_val)
    #     else:
    #         relative_diffs.append(0)
    print(f'''
===========================================
Valor relativo à {result.groupsunique[0]}..
===========================================
''')
    counter = 0
    for index, g in enumerate(result.groupsunique):
        for j in range(index+1, len(result.groupsunique)):
            media_g = comparative_values[column_name].loc[g]
            media_j = comparative_values[column_name].loc[result.groupsunique[j]]
            # relative_diff = relative_diffs[counter] # abordagem antiga
            relative_diff = result.meandiffs[counter]/media_g # comparando sempre o termo à esquerda

            diff = result.meandiffs[counter]

            if diff > 0:
                symbol = '\u2191'
            else:
                symbol = '\u2193'


            if result.reject[counter]:
                print(f'{g} ({media_g:.2f}) vs {result.groupsunique[j]} ({media_j:.2f})| {symbol} {abs(diff):.1f} | Diff. Relativa: {relative_diff:.0%}')
            else:
                print(f'{g} ({media_g:.2f}) vs {result.groupsunique[j]} ({media_j:.2f})| {symbol} {abs(diff):.1f} | Diff. Relativa: 0%')

            counter+=1

    # print('Só para confirmar...')
    # [print(f'Diff. Relativa: {diff:.0%}') for diff in relative_diffs]
    return result


def linePlot(df: pd.DataFrame, group: str, ax = Axes, debug = False):
    """
    Plots a stress-strain graph with a mean line and standard deviation dispersion
    grouped by a specific category.

    Parameters:
    - df: The pandas DataFrame containing the data.
          Must have 'STRAIN' and 'STRESS' columns.
    - group: The column name (string) to group the data by.
    """

    # Create the figure and axis
    if debug:
        fig2, ax2 = plt.subplots(figsize=(10, 6))

    # Discover the biggest x:
    x_max = 0
    for each_group in df[group].unique():
        df_local = df[df[group]==each_group]
        if df_local['STRAIN'].max() > x_max:
            x_max = df_local['STRAIN'].max()

    # Interpolate all the experimental results, to everyone lies on the same spot
    new_strain = []
    new_stress = []
    groups_for_new_df = []
    for each_id in df['ID'].unique():
        df_local = df[df['ID']==each_id]

        new_x = np.linspace(0, x_max, 10000)
        new_y = np.interp(new_x, df_local['STRAIN'], df_local['STRESS'])
        new_strain+=new_x.tolist()
        new_stress+=new_y.tolist()
        group_for_id = df_local[group].iloc[0]
        groups_for_new_df+=[group_for_id]*len(new_x)

        if debug:
            ax2.plot(new_x, new_y, linewidth=0.5, alpha=0.5, label=f'{each_id}|{group_for_id}|YEAR{df_local["YEAR"].iloc[0]}')


    # add the newest columns at the daframe
    data_to_plot = {
        'STRAIN': new_strain,
        'STRESS': new_stress,
        f'{group}': groups_for_new_df
    }
    new_df = pd.DataFrame(data_to_plot)

    for each_group  in new_df[group].unique():
        df_local = new_df[new_df[group]==each_group]
        stats = df_local.groupby(by=['STRAIN'])['STRESS'].agg(['mean','std']).reset_index()
        line, = ax.plot(stats['STRAIN'], stats['mean'], label=str(each_group), linewidth=2)

        ax.fill_between(
            stats['STRAIN'],
            stats['mean'] - stats['std'],
            stats['mean'] + stats['std'],
            color=line.get_color(),
            alpha=0.1  # Transparency for the shadow
        )

    ax.legend()

    if debug:
        ax2.legend()

    return ax
