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

"""Jury édition manuelle des décisions (correction d'erreurs, parcours hors normes)

Non spécifique au BUT.
"""

from flask import render_template
import sqlalchemy as sa

from app import log
from app.but import cursus_but
from app.models import (
    ApcCompetence,
    ApcNiveau,
    ApcReferentielCompetences,
    # ApcValidationAnnee, # TODO
    ApcValidationRCUE,
    Formation,
    FormSemestre,
    Identite,
    UniteEns,
    # ScolarAutorisationInscription,
    ScolarFormSemestreValidation,
)
from app.scodoc import codes_cursus
from app.scodoc.sco_exceptions import ScoNoReferentielCompetences
from app.views import ScoData


def validation_rcues(etud: Identite, formsemestre: FormSemestre, edit: bool = False):
    """Page de saisie des décisions de RCUEs "antérieures"
    On peut l'utiliser pour saisir la validation de n'importe quel RCUE
    d'une année antérieure et de la formation du formsemestre indiqué.
    """
    formation: Formation = formsemestre.formation
    refcomp = formation.referentiel_competence
    if refcomp is None:
        raise ScoNoReferentielCompetences(formation=formation)
    parcour = formsemestre.etuds_inscriptions[etud.id].parcour
    # Si non inscrit à un parcours, prend toutes les compétences
    competences_parcour = cursus_but.parcour_formation_competences(parcour, formation)

    ue_validation_by_niveau = get_ue_validation_by_niveau(refcomp, etud)
    rcue_validation_by_niveau = get_rcue_validation_by_niveau(refcomp, etud)
    ects_total = sum((v.ects() for v in ue_validation_by_niveau.values()))
    return render_template(
        "but/validation_rcues.j2",
        competences_parcour=competences_parcour,
        edit=edit,
        ects_total=ects_total,
        formation=formation,
        parcour=parcour,
        rcue_validation_by_niveau=rcue_validation_by_niveau,
        rcue_codes=sorted(codes_cursus.CODES_JURY_RCUE),
        sco=ScoData(formsemestre=formsemestre, etud=etud),
        title=f"{formation.acronyme} - Niveaux et UEs",
        ue_validation_by_niveau=ue_validation_by_niveau,
    )


def get_ue_validation_by_niveau(
    refcomp: ApcReferentielCompetences, etud: Identite
) -> dict[tuple[int, str], ScolarFormSemestreValidation]:
    """Les validations d'UEs de cet étudiant liées à ce référentiel de compétences.
    Pour chaque niveau / pair ou impair, choisi la "meilleure" validation
    """
    validations: list[ScolarFormSemestreValidation] = (
        ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
        .join(UniteEns)
        .join(ApcNiveau)
        .join(ApcCompetence)
        .filter_by(referentiel_id=refcomp.id)
        .all()
    )
    # La meilleure validation pour chaque UE
    ue_validation_by_niveau = {}  # { (niveau_id, pair|impair) : validation }
    for validation in validations:
        if validation.ue.niveau_competence is None:
            log(
                f"""validation_rcues: ignore validation d'UE {
                    validation.ue.id} pas de niveau de competence"""
            )
        key = (
            validation.ue.niveau_competence.id,
            "impair" if validation.ue.semestre_idx % 2 else "pair",
        )
        existing = ue_validation_by_niveau.get(key, None)
        if (not existing) or (
            codes_cursus.BUT_CODES_ORDER[existing.code]
            < codes_cursus.BUT_CODES_ORDER[validation.code]
        ):
            ue_validation_by_niveau[key] = validation
    return ue_validation_by_niveau


def get_rcue_validation_by_niveau(
    refcomp: ApcReferentielCompetences, etud: Identite
) -> dict[int, ApcValidationRCUE]:
    """Les validations d'UEs de cet étudiant liées à ce référentiel de compétences.
    Pour chaque niveau / pair ou impair, choisi la "meilleure" validation
    """
    validations: list[ApcValidationRCUE] = (
        ApcValidationRCUE.query.filter_by(etudid=etud.id)
        .join(UniteEns, UniteEns.id == ApcValidationRCUE.ue2_id)
        .join(ApcNiveau, UniteEns.niveau_competence_id == ApcNiveau.id)
        .join(ApcCompetence)
        .filter_by(referentiel_id=refcomp.id)
        .all()
    )
    return {
        validation.ue2.niveau_competence.id: validation for validation in validations
    }