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

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

from flask import g, jsonify
from flask_login import current_user
from flask_login import login_required
from sqlalchemy import desc, or_

import app
from app.api import api_bp as bp, api_web_bp
from app.scodoc.sco_utils import json_error
from app.api import tools
from app.decorators import scodoc, permission_required
from app.models import (
    Admission,
    Departement,
    FormSemestreInscription,
    FormSemestre,
    Identite,
)
from app.scodoc import sco_bulletins
from app.scodoc import sco_groups
from app.scodoc.sco_bulletins import do_formsemestre_bulletinetud
from app.scodoc.sco_permissions import Permission

# Un exemple:
# @bp.route("/api_function/<int:arg>")
# @api_web_bp.route("/api_function/<int:arg>")
# @login_required
# @scodoc
# @permission_required(Permission.ScoView)
# def api_function(arg: int):
#     """Une fonction quelconque de l'API"""
#     return jsonify(
#         {"current_user": current_user.to_dict(), "arg": arg, "dept": g.scodoc_dept}
#     )


@bp.route("/etudiants/courants", defaults={"long": False})
@bp.route("/etudiants/courants/long", defaults={"long": True})
@api_web_bp.route("/etudiants/courants", defaults={"long": False})
@api_web_bp.route("/etudiants/courants/long", defaults={"long": True})
@login_required
@scodoc
@permission_required(Permission.ScoView)
def etudiants_courants(long=False):
    """
    La liste des étudiants des semestres "courants" (tous départements)
    (date du jour comprise dans la période couverte par le sem.)
    dans lesquels l'utilisateur a la permission ScoView
    (donc tous si le dept du rôle est None).

    Exemple de résultat :
        [
        {
            "id": 1234,
            "code_nip": "12345678",
            "code_ine": null,
            "nom": "JOHN",
            "nom_usuel": None,
            "prenom": "DEUF",
            "civilite": "M",
        }
            ...
        ]

    En format "long": voir documentation.

    """
    allowed_depts = current_user.get_depts_with_permission(Permission.ScoView)
    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 not None in allowed_depts:
        # restreint aux départements autorisés:
        etuds = etuds.join(Departement).filter(
            or_(Departement.acronym == acronym for acronym in allowed_depts)
        )
    if long:
        data = [etud.to_dict_api() for etud in etuds]
    else:
        data = [etud.to_dict_short() for etud in etuds]
    return jsonify(data)


@bp.route("/etudiant/etudid/<int:etudid>")
@bp.route("/etudiant/nip/<string:nip>")
@bp.route("/etudiant/ine/<string:ine>")
@api_web_bp.route("/etudiant/etudid/<int:etudid>")
@api_web_bp.route("/etudiant/nip/<string:nip>")
@api_web_bp.route("/etudiant/ine/<string:ine>")
@login_required
@scodoc
@permission_required(Permission.ScoView)
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.
    """
    etud = tools.get_etud(etudid, nip, ine)

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

    return jsonify(etud.to_dict_api())


@bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"])
@bp.route("/etudiants/nip/<string:nip>", methods=["GET"])
@bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@api_web_bp.route("/etudiants/etudid/<int:etudid>", methods=["GET"])
@api_web_bp.route("/etudiants/nip/<string:nip>", methods=["GET"])
@api_web_bp.route("/etudiants/ine/<string:ine>", methods=["GET"])
@scodoc
@permission_required(Permission.ScoView)
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.).
    """
    allowed_depts = current_user.get_depts_with_permission(Permission.ScoView)
    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 json_error(
            404,
            message="parametre manquant",
        )
    if not None in allowed_depts:
        # restreint aux départements autorisés:
        etuds = etuds.join(Departement).filter(
            or_(Departement.acronym == acronym for acronym in allowed_depts)
        )
    return jsonify([etud.to_dict_api() 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")
@api_web_bp.route("/etudiant/etudid/<int:etudid>/formsemestres")
@api_web_bp.route("/etudiant/nip/<string:nip>/formsemestres")
@api_web_bp.route("/etudiant/ine/<string:ine>/formsemestres")
@scodoc
@permission_required(Permission.ScoView)
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.

    Attention, si accès via NIP ou INE, les semestres peuvent être de départements
    différents (si l'étudiant a changé de département). L'id du département est `dept_id`.

    Si accès par département, ne retourne que les formsemestre suivis dans le département.
    """
    if etudid is not None:
        q_etud = Identite.query.filter_by(id=etudid)
    elif nip is not None:
        q_etud = Identite.query.filter_by(code_nip=nip)
    elif ine is not None:
        q_etud = Identite.query.filter_by(code_ine=ine)
    else:
        return json_error(404, message="parametre manquant")
    if g.scodoc_dept is not None:
        q_etud = q_etud.filter_by(dept_id=g.scodoc_dept_id)
    etud = q_etud.join(Admission).order_by(desc(Admission.annee)).first()
    if etud is None:
        return json_error(404, message="etudiant inexistant")
    query = FormSemestre.query.filter(
        FormSemestreInscription.etudid == etud.id,
        FormSemestreInscription.formsemestre_id == FormSemestre.id,
    )
    if g.scodoc_dept is not None:
        query = query.filter_by(dept_id=g.scodoc_dept_id)

    formsemestres = query.order_by(FormSemestre.date_debut)

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


@bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long", "pdf": False},
)
@bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long", "pdf": False},
)
@bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long", "pdf": False},
)
@bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/pdf",
    methods=["GET"],
    defaults={"version": "long", "pdf": True},
)
@bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short", "pdf": False},
)
@bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short", "pdf": False},
)
@bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short", "pdf": False},
)
@bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
    methods=["GET"],
    defaults={"version": "short", "pdf": True},
)
@bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
    methods=["GET"],
    defaults={"version": "short", "pdf": True},
)
@bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
    methods=["GET"],
    defaults={"version": "short", "pdf": True},
)
@bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/pdf",
    methods=["GET"],
    defaults={"version": "long", "pdf": True},
)
@bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/pdf",
    methods=["GET"],
    defaults={"version": "long", "pdf": True},
)
@api_web_bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long", "pdf": False},
)
@api_web_bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long", "pdf": False},
)
@api_web_bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin",
    methods=["GET"],
    defaults={"version": "long", "pdf": False},
)
@api_web_bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/pdf",
    methods=["GET"],
    defaults={"version": "long", "pdf": True},
)
@api_web_bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short", "pdf": False},
)
@api_web_bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short", "pdf": False},
)
@api_web_bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short",
    methods=["GET"],
    defaults={"version": "short", "pdf": False},
)
@api_web_bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
    methods=["GET"],
    defaults={"version": "short", "pdf": True},
)
@api_web_bp.route(
    "/etudiant/nip/<string:nip>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
    methods=["GET"],
    defaults={"version": "short", "pdf": True},
)
@api_web_bp.route(
    "/etudiant/ine/<string:ine>/formsemestre/<int:formsemestre_id>/bulletin/short/pdf",
    methods=["GET"],
    defaults={"version": "short", "pdf": True},
)
@scodoc
@permission_required(Permission.ScoView)
def etudiant_bulletin_semestre(
    formsemestre_id,
    etudid: int = None,
    nip: str = None,
    ine: str = None,
    version="long",
    pdf: bool = False,
):
    """
    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 : voir https://scodoc.org/ScoDoc9API/#bulletin

    """
    formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first_or_404()
    dept = Departement.query.filter_by(id=formsemestre.dept_id).first_or_404()
    if g.scodoc_dept and dept.acronym != g.scodoc_dept:
        return json_error(404, "formsemestre non trouve")
    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 json_error(404, message="parametre manquant")

    etud = query.first()
    if etud is None:
        return json_error(404, message="etudiant inexistant")

    app.set_sco_dept(dept.acronym)

    if pdf:
        pdf_response, _ = do_formsemestre_bulletinetud(
            formsemestre, etud.id, version=version, format="pdf"
        )
        return pdf_response

    return sco_bulletins.get_formsemestre_bulletin_etud_json(
        formsemestre, etud, version=version
    )


@bp.route(
    "/etudiant/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>/groups",
    methods=["GET"],
)
@scodoc
@permission_required(Permission.ScoView)
def etudiant_groups(formsemestre_id: int, etudid: 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

    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"
    }
    ]
    """

    query = FormSemestre.query.filter_by(id=formsemestre_id)
    if g.scodoc_dept:
        query = query.filter_by(dept_id=g.scodoc_dept_id)
    formsemestre = query.first()
    if formsemestre is None:
        return json_error(
            404,
            message="formsemestre inconnu",
        )
    dept = formsemestre.departement
    etud = Identite.query.filter_by(id=etudid, dept_id=dept.id).first_or_404(etudid)

    app.set_sco_dept(dept.acronym)
    data = sco_groups.get_etud_groups(etud.id, formsemestre.id)

    return jsonify(data)