##############################################################################
# ScoDoc
# Copyright (c) 1999 - 2024 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).

CATEGORY
--------
Département

"""
from datetime import datetime

from flask import request
from flask_json import as_json
from flask_login import login_required

from app import db, log
from app.api import api_bp as bp, API_CLIENT_ERROR
from app.api import api_permission_required as permission_required
from app.decorators import scodoc
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
from app.scodoc.sco_utils import json_error


@bp.route("/departements")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departements_list():
    """Liste tous les départements.

    SAMPLES
    -------
    /departements;

    """
    return [dept.to_dict(with_dept_name=True) for dept in Departement.query]


@bp.route("/departements_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departements_ids():
    """Liste des ids de tous les départements.

    SAMPLES
    -------
    /departements_ids;

    """
    return [dept.id for dept in Departement.query]


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

    SAMPLES
    -------
    /departement/TAPI;

    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    return dept.to_dict(with_dept_name=True)


@bp.route("/departement/id/<int:dept_id>")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departement_get(dept_id: int):
    """
    Info sur un département. Accès par id.

    SAMPLES
    -------
    /departement/id/1;

    """
    dept = Departement.query.get_or_404(dept_id)
    return dept.to_dict()


@bp.route("/departement/create", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def departement_create():
    """
    Création d'un département.
    Le content type doit être `application/json`.
    DATA
    ----
    ```json
    {
        "acronym": str,
        "visible": bool,
    }
    ```

    SAMPLES
    -------
    /departement/create;{""acronym"":""MYDEPT"",""visible"":""1""}
    """
    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 "")

    log(f"departement_create {dept.acronym}")

    return dept.to_dict()


@bp.route("/departement/<string:acronym>/edit", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoSuperAdmin)
@as_json
def departement_edit(acronym):
    """
    Édition d'un département: seul le champ `visible` peut être modifié.

    DATA
    ----
    {
        "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()
    log(f"departement_edit {dept.acronym}")
    return 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 identifié par son acronyme.
    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    acronym = dept.acronym
    db.session.delete(dept)
    db.session.commit()
    log(f"departement_delete {acronym}")
    return {"OK": True}


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

    PARAMS
    ------
    acronym : l'acronyme d'un département

    SAMPLES
    -------
    /departement/TAPI/etudiants;

    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    return [etud.to_dict_short() for etud in dept.etudiants]


@bp.route("/departement/id/<int:dept_id>/etudiants")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departement_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 [etud.to_dict_short() for etud in dept.etudiants]


@bp.route("/departement/<string:acronym>/formsemestres_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departement_formsemestres_ids(acronym: str):
    """Liste des ids de tous les formsemestres du département.

    SAMPLES
    -------
    /departement/TAPI/formsemestres_ids;

    """
    dept = Departement.query.filter_by(acronym=acronym).first_or_404()
    return [formsemestre.id for formsemestre in dept.formsemestres]


@bp.route("/departement/id/<int:dept_id>/formsemestres_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departement_formsemestres_ids_by_id(dept_id: int):
    """Liste des ids de tous les formsemestres du département.

    SAMPLES
    -------
    /departement/id/1/formsemestres_ids;

    """
    dept = Departement.query.get_or_404(dept_id)
    return [formsemestre.id for formsemestre in dept.formsemestres]


@bp.route("/departement/<string:acronym>/formsemestres_courants")
@bp.route("/departement/id/<int:dept_id>/formsemestres_courants")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def departement_formsemestres_courants(acronym: str = "", dept_id: int | None = None):
    """
    Liste les formsemestres du département indiqué (par son acronyme ou son id)
    contenant la date courante, ou à défaut celle indiquée en argument
    (au format ISO).

    QUERY
    -----
    date_courante:<string:date_courante>

    SAMPLES
    -------
    /departement/id/1/formsemestres_courants?date_courante=2022-01-01
    """
    dept = (
        Departement.query.filter_by(acronym=acronym).first_or_404()
        if acronym
        else Departement.query.get_or_404(dept_id)
    )
    date_courante = request.args.get("date_courante")
    date_courante = datetime.fromisoformat(date_courante) if date_courante else None
    return [
        formsemestre.to_dict_api()
        for formsemestre in FormSemestre.get_dept_formsemestres_courants(
            dept, date_courante
        )
    ]