##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2022 Emmanuel Viennet.  All rights reserved.
# See LICENSE
##############################################################################

"""Résultats semestres BUT
"""
import time
import numpy as np
import pandas as pd

from app import log
from app.comp import moy_ue, moy_sem, inscr_mod
from app.comp.res_common import NotesTableCompat
from app.comp.bonus_spo import BonusSport
from app.models import ScoDocSiteConfig, formsemestre
from app.models.ues import UniteEns
from app.scodoc.sco_codes_parcours import UE_SPORT


class ResultatsSemestreBUT(NotesTableCompat):
    """Résultats BUT: organisation des calculs"""

    _cached_attrs = NotesTableCompat._cached_attrs + (
        "modimpl_coefs_df",
        "modimpls_evals_poids",
        "sem_cube",
    )

    def __init__(self, formsemestre):
        super().__init__(formsemestre)

        if not self.load_cached():
            t0 = time.time()
            self.compute()
            t1 = time.time()
            self.store()
            t2 = time.time()
            log(
                f"ResultatsSemestreBUT: cached formsemestre_id={formsemestre.id} ({(t1-t0):g}s +{(t2-t1):g}s)"
            )

    def compute(self):
        "Charge les notes et inscriptions et calcule les moyennes d'UE et gen."
        (
            self.sem_cube,
            self.modimpls_evals_poids,
            self.modimpls_results,
        ) = moy_ue.notes_sem_load_cube(self.formsemestre)
        self.modimpl_inscr_df = inscr_mod.df_load_modimpl_inscr(self.formsemestre)
        self.modimpl_coefs_df, _, _ = moy_ue.df_load_modimpl_coefs(
            self.formsemestre, modimpls=self.formsemestre.modimpls_sorted
        )
        # l'idx de la colonne du mod modimpl.id est
        #       modimpl_coefs_df.columns.get_loc(modimpl.id)
        # idx de l'UE: modimpl_coefs_df.index.get_loc(ue.id)

        # Masque de tous les modules _sauf_ les bonus (sport)
        modimpls_mask = [
            modimpl.module.ue.type != UE_SPORT
            for modimpl in self.formsemestre.modimpls_sorted
        ]

        self.etud_moy_ue = moy_ue.compute_ue_moys_apc(
            self.sem_cube,
            self.etuds,
            self.formsemestre.modimpls_sorted,
            self.ues,
            self.modimpl_inscr_df,
            self.modimpl_coefs_df,
            modimpls_mask,
        )
        # Les coefficients d'UE ne sont pas utilisés en APC
        self.etud_coef_ue_df = pd.DataFrame(
            0.0, index=self.etud_moy_ue.index, columns=self.etud_moy_ue.columns
        )

        # --- Modules de MALUS sur les UEs
        self.malus = moy_ue.compute_malus(
            self.formsemestre, self.sem_cube, self.ues, self.modimpl_inscr_df
        )
        self.etud_moy_ue -= self.malus

        # --- Bonus Sport & Culture
        if not all(modimpls_mask):  # au moins un module bonus
            bonus_class = ScoDocSiteConfig.get_bonus_sport_class()
            if bonus_class is not None:
                bonus: BonusSport = bonus_class(
                    self.formsemestre,
                    self.sem_cube,
                    self.ues,
                    self.modimpl_inscr_df,
                    self.modimpl_coefs_df.transpose(),
                    self.etud_moy_gen,
                    self.etud_moy_ue,
                )
                self.bonus_ues = bonus.get_bonus_ues()
                if self.bonus_ues is not None:
                    self.etud_moy_ue += self.bonus_ues  # somme les dataframes

        # Clippe toutes les moyennes d'UE dans [0,20]
        self.etud_moy_ue.clip(lower=0.0, upper=20.0, inplace=True)

        # Moyenne générale indicative:
        # (note: le bonus sport a déjà été appliqué aux moyennes d'UE, et impacte
        # donc la moyenne indicative)
        # self.etud_moy_gen = moy_sem.compute_sem_moys_apc_using_coefs(
        #    self.etud_moy_ue, self.modimpl_coefs_df
        # )
        self.etud_moy_gen = moy_sem.compute_sem_moys_apc_using_ects(
            self.etud_moy_ue,
            [ue.ects for ue in self.ues if ue.type != UE_SPORT],
            formation_id=self.formsemestre.formation_id,
        )
        # --- UE capitalisées
        self.apply_capitalisation()

        # --- Classements:
        self.compute_rangs()

    def get_etud_mod_moy(self, moduleimpl_id: int, etudid: int) -> float:
        """La moyenne de l'étudiant dans le moduleimpl
        En APC, il s'agit d'une moyenne indicative sans valeur.
        Result: valeur float (peut être naN) ou chaîne "NI" (non inscrit ou DEM)
        """
        mod_idx = self.modimpl_coefs_df.columns.get_loc(moduleimpl_id)
        etud_idx = self.etud_index[etudid]
        # moyenne sur les UE:
        if len(self.sem_cube[etud_idx, mod_idx]):
            return np.nanmean(self.sem_cube[etud_idx, mod_idx])
        return np.nan

    def compute_etud_ue_coef(self, etudid: int, ue: UniteEns) -> float:
        """Détermine le coefficient de l'UE pour cet étudiant.
        N'est utilisé que pour l'injection des UE capitalisées dans la
        moyenne générale.
        En BUT, c'est simple: Coef = somme des coefs des modules vers cette UE.
        (ne dépend pas des modules auxquels est inscrit l'étudiant, ).
        """
        return self.modimpl_coefs_df.loc[ue.id].sum()