# -*- coding: UTF-8 -*
"""ScoDoc models: moduleimpls
"""
import pandas as pd

from app import db
from app.comp import df_cache
from app.models import Identite, Module

import app.scodoc.notesdb as ndb
from app.scodoc import sco_utils as scu


class ModuleImpl(db.Model):
    """Mise en oeuvre d'un module pour une annee/semestre"""

    __tablename__ = "notes_moduleimpl"
    __table_args__ = (db.UniqueConstraint("formsemestre_id", "module_id"),)

    id = db.Column(db.Integer, primary_key=True)
    moduleimpl_id = db.synonym("id")
    module_id = db.Column(
        db.Integer,
        db.ForeignKey("notes_modules.id"),
    )
    formsemestre_id = db.Column(
        db.Integer,
        db.ForeignKey("notes_formsemestre.id"),
        index=True,
    )
    responsable_id = db.Column("responsable_id", db.Integer, db.ForeignKey("user.id"))
    # formule de calcul moyenne:
    computation_expr = db.Column(db.Text())

    evaluations = db.relationship("Evaluation", lazy="dynamic", backref="moduleimpl")
    enseignants = db.relationship(
        "User",
        secondary="notes_modules_enseignants",
        lazy="dynamic",
        backref="moduleimpl",
        viewonly=True,
    )

    def __init__(self, **kwargs):
        super(ModuleImpl, self).__init__(**kwargs)

    def __repr__(self):
        return f"<{self.__class__.__name__} {self.id} module={repr(self.module)}>"

    def get_evaluations_poids(self) -> pd.DataFrame:
        """Les poids des évaluations vers les UE (accès via cache)"""
        evaluations_poids = df_cache.EvaluationsPoidsCache.get(self.id)
        if evaluations_poids is None:
            from app.comp import moy_mod

            evaluations_poids, _ = moy_mod.load_evaluations_poids(self.id)
            df_cache.EvaluationsPoidsCache.set(self.id, evaluations_poids)
        return evaluations_poids

    def invalidate_evaluations_poids(self):
        """Invalide poids cachés"""
        df_cache.EvaluationsPoidsCache.delete(self.id)

    def check_apc_conformity(self) -> bool:
        """true si les poids des évaluations du module permettent de satisfaire
        les coefficients du PN.
        """
        if not self.module.formation.get_parcours().APC_SAE or (
            self.module.module_type != scu.ModuleType.RESSOURCE
            and self.module.module_type != scu.ModuleType.SAE
        ):
            return True
        from app.comp import moy_mod

        return moy_mod.moduleimpl_is_conforme(
            self,
            self.get_evaluations_poids(),
            self.module.formation.get_module_coefs(self.module.semestre_id),
        )

    def to_dict(self):
        """as a dict, with the same conversions as in ScoDoc7"""
        e = dict(self.__dict__)
        e.pop("_sa_instance_state", None)
        # ScoDoc7 output_formators: (backward compat)
        e["moduleimpl_id"] = self.id
        e["ens"] = [
            {"moduleimpl_id": self.id, "ens_id": e.id} for e in self.enseignants
        ]
        e["module"] = self.module.to_dict()
        return e


# Enseignants (chargés de TD ou TP) d'un moduleimpl
notes_modules_enseignants = db.Table(
    "notes_modules_enseignants",
    db.Column(
        "moduleimpl_id",
        db.Integer,
        db.ForeignKey("notes_moduleimpl.id"),
    ),
    db.Column("ens_id", db.Integer, db.ForeignKey("user.id")),
    # ? db.UniqueConstraint("moduleimpl_id", "ens_id"),
)
# XXX il manque probablement une relation pour gérer cela


class ModuleImplInscription(db.Model):
    """Inscription à un module  (etudiants,moduleimpl)"""

    __tablename__ = "notes_moduleimpl_inscription"
    __table_args__ = (db.UniqueConstraint("moduleimpl_id", "etudid"),)

    id = db.Column(db.Integer, primary_key=True)
    moduleimpl_inscription_id = db.synonym("id")
    moduleimpl_id = db.Column(
        db.Integer,
        db.ForeignKey("notes_moduleimpl.id"),
        index=True,
    )
    etudid = db.Column(db.Integer, db.ForeignKey("identite.id"), index=True)
    etud = db.relationship(
        Identite,
        backref=db.backref("moduleimpl_inscriptions", cascade="all, delete-orphan"),
    )
    modimpl = db.relationship(
        ModuleImpl,
        backref=db.backref("inscriptions", cascade="all, delete-orphan"),
    )

    @classmethod
    def nb_inscriptions_dans_ue(
        cls, formsemestre_id: int, etudid: int, ue_id: int
    ) -> int:
        """Nombre de moduleimpls de l'UE auxquels l'étudiant est inscrit"""
        return ModuleImplInscription.query.filter(
            ModuleImplInscription.etudid == etudid,
            ModuleImplInscription.moduleimpl_id == ModuleImpl.id,
            ModuleImpl.formsemestre_id == formsemestre_id,
            ModuleImpl.module_id == Module.id,
            Module.ue_id == ue_id,
        ).count()
