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

"""
  API : accès aux étudiants
"""

from flask import jsonify

import app
from app.api import bp
from app.api.errors import error_response
from app.api.auth import token_auth, token_permission_required
from app.models import Departement, FormSemestreInscription, FormSemestre, Identite
from app.scodoc import sco_bulletins
from app.scodoc import sco_groups
from app.scodoc.sco_permissions import Permission


@bp.route("/etudiants/courant", defaults={"long": False})
@bp.route("/etudiants/courant/long", defaults={"long": True})
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etudiants_courant(long=False):
    """
    Liste des étudiants inscrits dans un formsemestre actuellement en cours.

    Exemple de résultat :
        [
            {
                "id": 1,
                "nip": 1,
                "nom": "MOREL",
                "prenom": "JACQUES",
                "civilite": "X",
            },
            {
                "id": 2,
                "nip": 2,
                "nom": "GILLES",
                "prenom": "MAXIME",
                "civilite": "X",
            },
            ...
        ]
    """
    etuds = Identite.query.filter(
        Identite.id == FormSemestreInscription.etudid,
        FormSemestreInscription.formsemestre_id == FormSemestre.id,
        FormSemestre.date_debut <= app.db.func.now(),
        FormSemestre.date_fin >= app.db.func.now(),
    )
    if long:
        data = [etud.to_dict_bul(include_urls=False) for etud in etuds]
    else:
        data = [etud.to_dict_short() for etud in etuds]
    return jsonify(data)


@bp.route("/etudiant/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiant/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiant/ine/<string:ine>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etudiant(etudid: int = None, nip: str = None, ine: str = None):
    """
    Retourne les informations de l'étudiant correspondant, ou 404 si non trouvé.

    etudid : l'etudid de l'étudiant
    nip : le code nip de l'étudiant
    ine : le code ine de l'étudiant

    Les codes INE et NIP sont uniques au sein d'un département.
    Si plusieurs objets ont le même code, on ramène le plus récemment inscrit.

    Exemple de résultat :
    {
      "civilite": "X",
      "code_ine": "1",
      "code_nip": "1",
      "date_naissance": "",
      "email": "SACHA.COSTA@example.com",
      "emailperso": "",
      "etudid": 1,
      "nom": "COSTA",
      "prenom": "SACHA",
      "nomprenom": "Sacha COSTA",
      "lieu_naissance": "",
      "dept_naissance": "",
      "nationalite": "",
      "boursier": "",
      "id": 1,
      "codepostaldomicile": "",
      "paysdomicile": "",
      "telephonemobile": "",
      "typeadresse": "domicile",
      "domicile": "",
      "villedomicile": "",
      "telephone": "",
      "fax": "",
      "description": ""
    }
    """
    if etudid is not None:
        etud = Identite.query.get(etudid)
    else:
        if nip is not None:
            query = Identite.query.filter_by(code_nip=nip)
        elif ine is not None:
            query = Identite.query.filter_by(code_ine=ine)
        else:
            return error_response(
                404,
                message="parametre manquant",
            )
        if query.count() > 1:  # cas rare d'un étudiant présent dans plusieurs depts
            etuds = []
            for e in query:
                admission = e.admission.first()
                etuds.append((((admission.annee or 0) if admission else 0), e))
            etuds.sort()
            etud = etuds[-1][1]
        else:
            etud = query.first()

    if etud is None:
        return error_response(
            404,
            message="étudiant inconnu",
        )

    return jsonify(etud.to_dict_bul(include_urls=False))


@bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiants/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etudiants(etudid: int = None, nip: str = None, ine: str = None):
    """
    Info sur le ou les étudiants correspondant. Comme /etudiant mais renvoie
    toujours une liste.
    Si non trouvé, liste vide, pas d'erreur.
    Dans 99% des cas, la liste contient un seul étudiant, mais si l'étudiant a
    été inscrit dans plusieurs départements, on a plusieurs objets (1 par dept.).
    """
    if etudid is not None:
        query = Identite.query.filter_by(id=etudid)
    elif nip is not None:
        query = Identite.query.filter_by(code_nip=nip)
    elif ine is not None:
        query = Identite.query.filter_by(code_ine=ine)
    else:
        return error_response(
            404,
            message="parametre manquant",
        )

    return jsonify([etud.to_dict_bul(include_urls=False) for etud in query])


@bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
@bp.route("/etudiant/nip/<string:nip>/formsemestres")
@bp.route("/etudiant/ine/<string:ine>/formsemestres")
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etudiant_formsemestres(etudid: int = None, nip: int = None, ine: int = None):
    """
    Liste des semestres qu'un étudiant a suivi, triés par ordre chronologique.
    Accès par etudid, nip ou ine

    Exemple de résultat :
    [
        {
          "date_fin": "31/08/2022",
          "resp_can_edit": false,
          "dept_id": 1,
          "etat": true,
          "resp_can_change_ens": true,
          "id": 1,
          "modalite": "FI",
          "ens_can_edit_eval": false,
          "formation_id": 1,
          "gestion_compensation": false,
          "elt_sem_apo": null,
          "semestre_id": 1,
          "bul_hide_xml": false,
          "elt_annee_apo": null,
          "titre": "Semestre test",
          "block_moyennes": false,
          "scodoc7_id": null,
          "date_debut": "01/09/2021",
          "gestion_semestrielle": false,
          "bul_bgcolor": "white",
          "formsemestre_id": 1,
          "titre_num": "Semestre test semestre 1",
          "date_debut_iso": "2021-09-01",
          "date_fin_iso": "2022-08-31",
          "responsables": []
        },
          ...
    ]
    """
    if etudid is not None:
        query = FormSemestre.query.filter(
            FormSemestreInscription.etudid == etudid,
            FormSemestreInscription.formsemestre_id == FormSemestre.id,
        )
    elif nip is not None:
        query = FormSemestre.query.filter(
            Identite.code_nip == nip,
            FormSemestreInscription.etudid == Identite.id,
            FormSemestreInscription.formsemestre_id == FormSemestre.id,
        )
    elif ine is not None:
        query = FormSemestre.query.filter(
            Identite.code_ine == ine,
            FormSemestreInscription.etudid == Identite.id,
            FormSemestreInscription.formsemestre_id == FormSemestre.id,
        )
    else:
        return error_response(
            404,
            message="parametre manquant",
        )

    formsemestres = query.order_by(FormSemestre.date_debut)

    return jsonify([formsemestre.to_dict() for formsemestre in formsemestres])


@bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long"},
)
@bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long"},
)
@bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long"},
)
@bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short"},
)
@bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short"},
)
@bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short"},
)
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etudiant_bulletin_semestre(
    formsemestre_id,
    etudid: int = None,
    nip: str = None,
    ine: str = None,
    version="long",
):
    """
    Retourne le bulletin d'un étudiant en fonction de son id et d'un semestre donné

    formsemestre_id : l'id d'un formsemestre
    etudid : l'etudid d'un étudiant
    nip : le code nip d'un étudiant
    ine : le code ine d'un étudiant
    Exemple de résultat :
    {
        "version": "0",
        "type": "BUT",
        "date": "2022-04-27T07:18:16.450634Z",
        "publie": true,
        "etudiant": {
            "civilite": "X",
            "code_ine": "1",
            "code_nip": "1",
            "date_naissance": "",
            "email": "SACHA.COSTA@example.com",
            "emailperso": "",
            "etudid": 1,
            "nom": "COSTA",
            "prenom": "SACHA",
            "nomprenom": "Sacha COSTA",
            "lieu_naissance": "",
            "dept_naissance": "",
            "nationalite": "",
            "boursier": "",
            "fiche_url": "/ScoDoc/TAPI/Scolarite/ficheEtud?etudid=1",
            "photo_url": "/ScoDoc/TAPI/Scolarite/get_photo_image?etudid=1&size=small",
            "id": 1,
            "codepostaldomicile": "",
            "paysdomicile": "",
            "telephonemobile": "",
            "typeadresse": "domicile",
            "domicile": "",
            "villedomicile": "",
            "telephone": "",
            "fax": "",
            "description": "",
        },
        "formation": {
            "id": 1,
            "acronyme": "BUT R&amp;T",
            "titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
            "titre": "BUT R&amp;T",
        },
        "formsemestre_id": 1,
        "etat_inscription": "I",
        "options": {
            "show_abs": true,
            "show_abs_modules": false,
            "show_ects": true,
            "show_codemodules": false,
            "show_matieres": false,
            "show_rangs": true,
            "show_ue_rangs": true,
            "show_mod_rangs": true,
            "show_moypromo": false,
            "show_minmax": false,
            "show_minmax_mod": false,
            "show_minmax_eval": false,
            "show_coef": true,
            "show_ue_cap_details": false,
            "show_ue_cap_current": true,
            "show_temporary": true,
            "temporary_txt": "Provisoire",
            "show_uevalid": true,
            "show_date_inscr": true,
        },
        "ressources": {
            "R101": {
                "id": 1,
                "titre": "Initiation aux r\u00e9seaux informatiques",
                "code_apogee": null,
                "url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=1",
                "moyenne": {},
                "evaluations": [
                    {
                        "id": 1,
                        "description": "eval1",
                        "date": "2022-04-20",
                        "heure_debut": "08:00",
                        "heure_fin": "09:00",
                        "coef": "01.00",
                        "poids": {
                            "RT1.1": 1.0,
                        },
                        "note": {
                            "value": "12.00",
                            "min": "00.00",
                            "max": "18.00",
                            "moy": "10.88",
                        },
                        "url": "/ScoDoc/TAPI/Scolarite/Notes/evaluation_listenotes?evaluation_id=1",
                    }
                ],
            },
        },
        "saes": {
            "SAE11": {
                "id": 2,
                "titre": "Se sensibiliser \u00e0 l&apos;hygi\u00e8ne informatique et \u00e0 la cybers\u00e9curit\u00e9",
                "code_apogee": null,
                "url": "/ScoDoc/TAPI/Scolarite/Notes/moduleimpl_status?moduleimpl_id=2",
                "moyenne": {},
                "evaluations": [],
            },
        },
        "ues": {
            "RT1.1": {
                "id": 1,
                "titre": "Administrer les r\u00e9seaux et l\u2019Internet",
                "numero": 1,
                "type": 0,
                "color": "#B80004",
                "competence": null,
                "moyenne": {
                    "value": "08.50",
                    "min": "06.00",
                    "max": "16.50",
                    "moy": "11.31",
                    "rang": "12",
                    "total": 16,
                },
                "bonus": "00.00",
                "malus": "00.00",
                "capitalise": null,
                "ressources": {
                    "R101": {"id": 1, "coef": 12.0, "moyenne": "12.00"},
                },
                "saes": {
                    "SAE11": {"id": 2, "coef": 16.0, "moyenne": "~"},
                },
                "ECTS": {"acquis": 0.0, "total": 12.0},
            },
            "semestre": {
                "etapes": [],
                "date_debut": "2021-09-01",
                "date_fin": "2022-08-31",
                "annee_universitaire": "2021 - 2022",
                "numero": 1,
                "inscription": "",
                "groupes": [],
                "absences": {"injustifie": 1, "total": 2},
                "ECTS": {"acquis": 0, "total": 30.0},
                "notes": {"value": "10.60", "min": "02.40", "moy": "11.05", "max": "17.40"},
                "rang": {"value": "10", "total": 16},
            },
        },
    }
    """
    formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
    dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()

    if etudid is not None:
        query = Identite.query.filter_by(id=etudid)
    elif nip is not None:
        query = Identite.query.filter_by(code_nip=nip, dept_id=dept.id)
    elif ine is not None:
        query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
    else:
        return error_response(
            404,
            message="parametre manquant",
        )

    etud = query.first()
    if etud is None:
        return error_response(
            404,
            message="id de l'étudiant (etudid, nip, ine) inconnu",
        )

    app.set_sco_dept(dept.acronym)
    return sco_bulletins.get_formsemestre_bulletin_etud_json(
        formsemestre, etud, version
    )


@bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/groups",
    methods=["GET"],
)
@bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/groups",
    methods=["GET"],
)
@bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/groups",
    methods=["GET"],
)
@token_auth.login_required
@token_permission_required(Permission.APIView)
def etudiant_groups(
    formsemestre_id: int, etudid: int = None, nip: int = None, ine: int = None
):
    """
    Retourne la liste des groupes auxquels appartient l'étudiant dans le formsemestre indiqué

    formsemestre_id : l'id d'un formsemestre
    etudid : l'etudid d'un étudiant
    nip : le code nip d'un étudiant
    ine : le code ine d'un étudiant

    Exemple de résultat :
    [
    {
        "partition_id": 1,
        "id": 1,
        "formsemestre_id": 1,
        "partition_name": null,
        "numero": 0,
        "bul_show_rank": false,
        "show_in_lists": true,
        "group_id": 1,
        "group_name": null
    },
    {
        "partition_id": 2,
        "id": 2,
        "formsemestre_id": 1,
        "partition_name": "TD",
        "numero": 1,
        "bul_show_rank": false,
        "show_in_lists": true,
        "group_id": 2,
        "group_name": "A"
    }
    ]
    """

    formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
    if formsemestre is None:
        return error_response(
            404,
            message="formsemestre inconnu",
        )
    dept = Departement.query.get(formsemestre.dept_id)
    if etudid is not None:
        query = Identite.query.filter_by(id=etudid)
    elif nip is not None:
        query = Identite.query.filter_by(code_nip=nip, dept_id=dept.id)
    elif ine is not None:
        query = Identite.query.filter_by(code_ine=ine, dept_id=dept.id)
    else:
        return error_response(
            404,
            message="parametre manquant",
        )
    etud = query.first()
    if etud is None:
        return error_response(
            404,
            message="etudiant inconnu",
        )
    app.set_sco_dept(dept.acronym)
    data = sco_groups.get_etud_groups(etud.id, formsemestre.id)

    return jsonify(data)