import pandas as pd
import numpy as np
from io import BytesIO


pandas_json_format = 'table'


class dtypes:
    students_table = {'Examplan.id': 'int64', 'PrüfungsNr.': 'int64', 'Titel': 'O', 'Nachname': 'O', 'Vorname': 'O', 'Leistung': 'O', 'Versuch': 'int64', 'Status': 'O', 'ECTS': 'O', 'Semester': 'O', 'Jahr': 'int64', 'Prüfungsperiode': 'int64', 'Vermerk': 'O', 'Thema': 'O', 'Beginn': 'O', 'Gepl. Ende': 'O', 'Tatsächl. Ende': 'O', 'Prüfungsart': 'O', 'Prüfungsform': 'O', 'LockVersion': 'O'}
    his_header = 'O'


def read_excel(bytes: BytesIO) -> tuple[pd.DataFrame, pd.DataFrame]:
    try:
        excelfile = pd.ExcelFile(bytes, engine='openpyxl')
    except:
        return pd.DataFrame()

    full_table = pd.read_excel(excelfile, header=None)

    # Finde Indizes der Zeilen mit den Werten "startHISsheet" und "endHISsheet"
    def find_row_with_value(df, value):
        mask = full_table.iloc[:, 0] == value
        if not mask.any():
            raise ValueError(f'{value} nicht gefunden')
        return mask.idxmax()

    start_idx = find_row_with_value(full_table, 'startHISsheet')
    end_idx = find_row_with_value(full_table, 'endHISsheet')

    # Lade den HISinOne Header (die ersten start_idx+1 Zeilen)
    his_header = pd.read_excel(excelfile, nrows=start_idx+1, header=None)

    # Lade Studentendaten (zwischen start_idx+1 und end_idx)
    students_table = pd.read_excel(excelfile,
                                   header=start_idx + 1,
                                   nrows=end_idx - start_idx - 2,
                                   index_col='Matrikelnummer',
                                   dtype=dtypes.students_table)
    
    students_table = students_table.replace({np.nan: None})
    his_header = his_header.replace({np.nan: None})
    
    return students_table, his_header


def prepare_table(students_table: pd.DataFrame) -> pd.DataFrame:

    # Erzeuge Tabelle zur Punkteeingabe
    points_table = students_table

    new_columns = {'Punkte': np.nan, 'Note': np.nan}
    points_table = points_table.assign(**new_columns)

    return points_table


def export(edited_df: pd.DataFrame, his_header: pd.DataFrame, exercise_columns: list[str], grades_table: pd.DataFrame) -> BytesIO:
    edited_df['Leistung'] = edited_df['Note'].replace({np.nan: 'NT'})
    export_students_table = edited_df.drop(columns=['Punkte', 'Note']+exercise_columns).reset_index()

    # Examplan.id muss erste Spalte sein (HISinOne prüft Spaltennamen nicht)
    export_students_table = export_students_table.reindex(columns=['Examplan.id'] + [col for col in export_students_table.columns if col != 'Examplan.id'])

    export_full_table = pd.DataFrame(
        np.vstack([
            his_header.values,                                                          # HISinOne Header
            np.array(list(export_students_table)),                                      # Tabellenheader (Matrikelnummer, Nachname, Vorname, ...)
            export_students_table.values,                                               # Daten
            np.array(['endHISsheet'] + [''] * (len(list(export_students_table))-1))     # HISinOne Footer
            ]),
        columns=export_students_table.columns)

    # Exportiere Tabelle
    bytesio = BytesIO()
    with pd.ExcelWriter(bytesio, engine='openpyxl') as writer:
        export_full_table.to_excel(writer, sheet_name='First Sheet', header=False, index=False)                        # HISinOne Tabelle
        edited_df[['Nachname', 'Vorname', 'Punkte', 'Note']+exercise_columns].to_excel(writer, sheet_name='Aufgaben')  # Punktergebnisse als extra Sheet
        grades_table.to_excel(writer, sheet_name='Notenschlüssel', header=False, index=False)                                                              # Notenberechnung als extra Sheet
    return bytesio
