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

"""
  ScoDoc 9 API : accès aux départements

  Note: les routes /departement[s] sont publiées sur l'API (/ScoDoc/api/), 
  mais évidemment pas sur l'API web (/ScoDoc/<dept>/api).
"""
from datetime import datetime

from flask import jsonify, request
from flask_login import login_required

import app
from app import db
from app.api import api_bp as bp, API_CLIENT_ERROR
from app.scodoc.sco_utils import json_error
from app.decorators import scodoc, permission_required
from app.models import Departement, FormSemestre
from app.models import departements
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission


def get_departement(dept_ident: str) -> Departement:
    "Le departement, par id ou acronyme. Erreur 404 si pas trouvé."
    try:
        dept_id = int(dept_ident)
    except ValueError:
        dept_id = None
    if dept_id is None:
        return Departement.query.filter_by(acronym=dept_ident).first_or_404()
    return Departement.query.get_or_404(dept_id)


@bp.route("/departements")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def departements_list():
    """Liste les départements"""
    return jsonify([dept.to_dict(with_dept_name=True) for dept in Departement.query])


@bp.route("/departements_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def departements_ids():
    """Liste des ids de départements"""
    return jsonify([dept.id for dept in Departement.query])


@bp.route("/departement/<string:acronym>")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def departement(acronym: str):
    """
    Info sur un département. Accès par acronyme.

    Exemple de résultat :
          {
            "id": 1,
            "acronym": "TAPI",
            "dept_name" : "TEST",
            "description": null,
            "visible": true,
            "date_creation": "Fri, 15 Apr 2022 12:19:28 GMT"
          }
    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    return jsonify(dept.to_dict(with_dept_name=True))


@bp.route("/departement/id/<int:dept_id>")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def departement_by_id(dept_id: int):
    """
    Info sur un département. Accès par id.
    """
    dept = Departement.query.get_or_404(dept_id)
    return jsonify(dept.to_dict())


@bp.route("/departement/create", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
def departement_create():
    """
    Création d'un département.
    The request content type should be "application/json":
    {
        "acronym": str,
        "visible":bool,
    }
    """
    data = request.get_json(force=True)  # may raise 400 Bad Request
    acronym = str(data.get("acronym", ""))
    if not acronym:
        return json_error(API_CLIENT_ERROR, "missing acronym")
    visible = bool(data.get("visible", True))
    try:
        dept = departements.create_dept(acronym, visible=visible)
    except ScoValueError as exc:
        return json_error(500, exc.args[0] if exc.args else "")
    return jsonify(dept.to_dict())


@bp.route("/departement/<string:acronym>/edit", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
def departement_edit(acronym):
    """
    Edition d'un département: seul visible peut être modifié
    The request content type should be "application/json":
    {
        "visible":bool,
    }
    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    data = request.get_json(force=True)  # may raise 400 Bad Request
    visible = bool(data.get("visible", None))
    if visible is None:
        return json_error(API_CLIENT_ERROR, "missing argument: visible")
    visible = bool(visible)
    dept.visible = visible
    db.session.add(dept)
    db.session.commit()
    return jsonify(dept.to_dict())


@bp.route("/departement/<string:acronym>/delete", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
def departement_delete(acronym):
    """
    Suppression d'un département.
    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    db.session.delete(dept)
    db.session.commit()
    return jsonify({"OK": True})


@bp.route("/departement/<string:acronym>/etudiants", methods=["GET"])
@login_required
@scodoc
@permission_required(Permission.ScoView)
def dept_etudiants(acronym: str):
    """
    Retourne la liste des étudiants d'un département

    acronym: l'acronyme d'un département

    Exemple de résultat :
        [
            {
                "civilite": "M",
                "code_ine": "7899X61616",
                "code_nip": "F6777H88",
                "date_naissance": null,
                "email": "toto@toto.fr",
                "emailperso": null,
                "etudid": 18,
                "nom": "MOREL",
                "prenom": "JACQUES"
            },
            ...
        ]
    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    return jsonify([etud.to_dict_short() for etud in dept.etudiants])


@bp.route("/departement/id/<int:dept_id>/etudiants")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def dept_etudiants_by_id(dept_id: int):
    """
    Retourne la liste des étudiants d'un département d'id donné.
    """
    dept = Departement.query.get_or_404(dept_id)
    return jsonify([etud.to_dict_short() for etud in dept.etudiants])


@bp.route("/departement/<string:acronym>/formsemestres_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def dept_formsemestres_ids(acronym: str):
    """liste des ids formsemestre du département"""
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    return jsonify([formsemestre.id for formsemestre in dept.formsemestres])


@bp.route("/departement/id/<int:dept_id>/formsemestres_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def dept_formsemestres_ids_by_id(dept_id: int):
    """liste des ids formsemestre du département"""
    dept = Departement.query.get_or_404(dept_id)
    return jsonify([formsemestre.id for formsemestre in dept.formsemestres])


@bp.route("/departement/<string:acronym>/formsemestres_courants")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def dept_formsemestres_courants(acronym: str):
    """
    Liste des semestres actifs d'un département d'acronyme donné

    Exemple de résultat :
        [
          {
            "titre": "master machine info",
            "gestion_semestrielle": false,
            "scodoc7_id": null,
            "date_debut": "01/09/2021",
            "bul_bgcolor": null,
            "date_fin": "15/12/2022",
            "resp_can_edit": false,
            "dept_id": 1,
            "etat": true,
            "resp_can_change_ens": false,
            "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,
            "block_moyennes": false,
            "formsemestre_id": 1,
            "titre_num": "master machine info semestre 1",
            "date_debut_iso": "2021-09-01",
            "date_fin_iso": "2022-12-15",
            "responsables": [
              3,
              2
            ]
          },
          ...
        ]
    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    date_courante = request.args.get("date_courante")
    if date_courante:
        test_date = datetime.fromisoformat(date_courante)
    else:
        test_date = app.db.func.now()
    # Les semestres en cours de ce département
    formsemestres = FormSemestre.query.filter(
        FormSemestre.dept_id == dept.id,
        FormSemestre.date_debut <= test_date,
        FormSemestre.date_fin >= test_date,
    )
    return jsonify([d.to_dict_api() for d in formsemestres])


@bp.route("/departement/id/<int:dept_id>/formsemestres_courants")
@login_required
@scodoc
@permission_required(Permission.ScoView)
def dept_formsemestres_courants_by_id(dept_id: int):
    """
    Liste des semestres actifs d'un département d'id donné
    """
    # Le département, spécifié par un id ou un acronyme
    dept = Departement.query.get_or_404(dept_id)
    date_courante = request.args.get("date_courante")
    if date_courante:
        test_date = datetime.fromisoformat(date_courante)
    else:
        test_date = app.db.func.now()
    # Les semestres en cours de ce département
    formsemestres = FormSemestre.query.filter(
        FormSemestre.dept_id == dept.id,
        FormSemestre.date_debut <= test_date,
        FormSemestre.date_fin >= test_date,
    )

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