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

"""
  ScoDoc 9 API : accès aux formations
"""

from flask import flash, g, request
from flask_json import as_json
from flask_login import login_required

import app
from app import db, log
from app.api import api_bp as bp, api_web_bp
from app.scodoc.sco_utils import json_error
from app.decorators import scodoc, permission_required
from app.models import (
    ApcNiveau,
    ApcParcours,
    Formation,
    FormSemestre,
    ModuleImpl,
    UniteEns,
)
from app.scodoc import sco_formations
from app.scodoc.sco_permissions import Permission


@bp.route("/formations")
@api_web_bp.route("/formations")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formations():
    """
    Retourne la liste de toutes les formations (tous départements)
    """
    query = Formation.query
    if g.scodoc_dept:
        query = query.filter_by(dept_id=g.scodoc_dept_id)

    return [d.to_dict() for d in query]


@bp.route("/formations_ids")
@api_web_bp.route("/formations_ids")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formations_ids():
    """
    Retourne la liste de toutes les id de formations (tous départements)

    Exemple de résultat : [ 17, 99, 32 ]
    """
    query = Formation.query
    if g.scodoc_dept:
        query = query.filter_by(dept_id=g.scodoc_dept_id)
    return [d.id for d in query]


@bp.route("/formation/<int:formation_id>")
@api_web_bp.route("/formation/<int:formation_id>")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formation_by_id(formation_id: int):
    """
    La formation d'id donné

    formation_id : l'id d'une formation

    Exemple de résultat :
        {
          "id": 1,
          "acronyme": "BUT R&amp;T",
          "titre_officiel": "Bachelor technologique réseaux et télécommunications",
          "formation_code": "V1RET",
          "code_specialite": null,
          "dept_id": 1,
          "titre": "BUT R&amp;T",
          "version": 1,
          "type_parcours": 700,
          "referentiel_competence_id": null,
          "formation_id": 1
        }
    """
    query = Formation.query.filter_by(id=formation_id)
    if g.scodoc_dept:
        query = query.filter_by(dept_id=g.scodoc_dept_id)
    return query.first_or_404().to_dict()


@bp.route(
    "/formation/<int:formation_id>/export",
    defaults={"export_ids": False},
)
@bp.route(
    "/formation/<int:formation_id>/export_with_ids",
    defaults={"export_ids": True},
)
@api_web_bp.route(
    "/formation/<int:formation_id>/export",
    defaults={"export_ids": False},
)
@api_web_bp.route(
    "/formation/<int:formation_id>/export_with_ids",
    defaults={"export_ids": True},
)
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def formation_export_by_formation_id(formation_id: int, export_ids=False):
    """
    Retourne la formation, avec UE, matières, modules

    formation_id : l'id d'une formation
    export_ids : True ou False, si l'on veut ou non exporter les ids

    Exemple de résultat :
        {
          "id": 1,
          "acronyme": "BUT R&amp;T",
          "titre_officiel": "Bachelor technologique r\u00e9seaux et t\u00e9l\u00e9communications",
          "formation_code": "V1RET",
          "code_specialite": null,
          "dept_id": 1,
          "titre": "BUT R&amp;T",
          "version": 1,
          "type_parcours": 700,
          "referentiel_competence_id": null,
          "formation_id": 1,
          "ue": [
            {
              "acronyme": "RT1.1",
              "numero": 1,
              "titre": "Administrer les r\u00e9seaux et l\u2019Internet",
              "type": 0,
              "ue_code": "UCOD11",
              "ects": 12.0,
              "is_external": false,
              "code_apogee": "",
              "coefficient": 0.0,
              "semestre_idx": 1,
              "color": "#B80004",
              "reference": 1,
              "matiere": [
                {
                  "titre": "Administrer les r\u00e9seaux et l\u2019Internet",
                  "numero": 1,
                  "module": [
                    {
                      "titre": "Initiation aux r\u00e9seaux informatiques",
                      "abbrev": "Init aux r\u00e9seaux informatiques",
                      "code": "R101",
                      "heures_cours": 0.0,
                      "heures_td": 0.0,
                      "heures_tp": 0.0,
                      "coefficient": 1.0,
                      "ects": "",
                      "semestre_id": 1,
                      "numero": 10,
                      "code_apogee": "",
                      "module_type": 2,
                      "coefficients": [
                        {
                          "ue_reference": "1",
                          "coef": "12.0"
                        },
                        {
                          "ue_reference": "2",
                          "coef": "4.0"
                        },
                        {
                          "ue_reference": "3",
                          "coef": "4.0"
                        }
                      ]
                    },
                    {
                      "titre": "Se sensibiliser \u00e0 l&apos;hygi\u00e8ne informatique...",
                      "abbrev": "Hygi\u00e8ne informatique",
                      "code": "SAE11",
                      "heures_cours": 0.0,
                      "heures_td": 0.0,
                      "heures_tp": 0.0,
                      "coefficient": 1.0,
                      "ects": "",
                      "semestre_id": 1,
                      "numero": 10,
                      "code_apogee": "",
                      "module_type": 3,
                      "coefficients": [
                        {
                          "ue_reference": "1",
                          "coef": "16.0"
                        }
                      ]
                    },
                    ...
                  ]
                },
                ...
              ]
            },
          ]
        }
    """
    query = Formation.query.filter_by(id=formation_id)
    if g.scodoc_dept:
        query = query.filter_by(dept_id=g.scodoc_dept_id)
    formation = query.first_or_404(formation_id)
    app.set_sco_dept(formation.departement.acronym)
    try:
        data = sco_formations.formation_export(formation_id, export_ids)
    except ValueError:
        return json_error(500, message="Erreur inconnue")

    return data


@bp.route("/formation/<int:formation_id>/referentiel_competences")
@api_web_bp.route("/formation/<int:formation_id>/referentiel_competences")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def referentiel_competences(formation_id: int):
    """
    Retourne le référentiel de compétences

    formation_id : l'id d'une formation

    return null si pas de référentiel associé.
    """
    query = Formation.query.filter_by(id=formation_id)
    if g.scodoc_dept:
        query = query.filter_by(dept_id=g.scodoc_dept_id)
    formation = query.first_or_404(formation_id)
    if formation.referentiel_competence is None:
        return None
    return formation.referentiel_competence.to_dict()


@bp.route("/moduleimpl/<int:moduleimpl_id>")
@api_web_bp.route("/moduleimpl/<int:moduleimpl_id>")
@login_required
@scodoc
@permission_required(Permission.ScoView)
@as_json
def moduleimpl(moduleimpl_id: int):
    """
    Retourne un moduleimpl en fonction de son id

    moduleimpl_id : l'id d'un moduleimpl

    Exemple de résultat :
        {
          "id": 1,
          "formsemestre_id": 1,
          "module_id": 1,
          "responsable_id": 2,
          "moduleimpl_id": 1,
          "ens": [],
          "module": {
            "heures_tp": 0,
            "code_apogee": "",
            "titre": "Initiation aux réseaux informatiques",
            "coefficient": 1,
            "module_type": 2,
            "id": 1,
            "ects": null,
            "abbrev": "Init aux réseaux informatiques",
            "ue_id": 1,
            "code": "R101",
            "formation_id": 1,
            "heures_cours": 0,
            "matiere_id": 1,
            "heures_td": 0,
            "semestre_id": 1,
            "numero": 10,
            "module_id": 1
          }
        }
    """
    query = ModuleImpl.query.filter_by(id=moduleimpl_id)
    if g.scodoc_dept:
        query = query.join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
    modimpl: ModuleImpl = query.first_or_404()
    return modimpl.to_dict(convert_objects=True)


@bp.route("/set_ue_parcours/<int:ue_id>", methods=["POST"])
@api_web_bp.route("/set_ue_parcours/<int:ue_id>", methods=["POST"])
@login_required
@scodoc
@permission_required(Permission.ScoChangeFormation)
@as_json
def set_ue_parcours(ue_id: int):
    """Associe UE et parcours BUT.
    La liste des ids de parcours est passée en argument JSON.
    JSON arg: [parcour_id1, parcour_id2, ...]
    """
    query = UniteEns.query.filter_by(id=ue_id)
    if g.scodoc_dept:
        query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
    ue: UniteEns = query.first_or_404()
    parcours_ids = request.get_json(force=True) or []  # may raise 400 Bad Request
    if parcours_ids == [""]:
        parcours = []
    else:
        parcours = [
            ApcParcours.query.get_or_404(int(parcour_id)) for parcour_id in parcours_ids
        ]
    log(f"set_ue_parcours: ue_id={ue.id} parcours_ids={parcours_ids}")
    ok, error_message = ue.set_parcours(parcours)
    return {"status": ok, "message": error_message}


@bp.route(
    "/assoc_ue_niveau/<int:ue_id>/<int:niveau_id>",
    methods=["POST"],
)
@api_web_bp.route(
    "/assoc_ue_niveau/<int:ue_id>/<int:niveau_id>",
    methods=["POST"],
)
@login_required
@scodoc
@permission_required(Permission.ScoChangeFormation)
@as_json
def assoc_ue_niveau(ue_id: int, niveau_id: int):
    """Associe l'UE au niveau de compétence"""
    query = UniteEns.query.filter_by(id=ue_id)
    if g.scodoc_dept:
        query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
    ue: UniteEns = query.first_or_404()
    niveau: ApcNiveau = ApcNiveau.query.get_or_404(niveau_id)
    ok, error_message = ue.set_niveau_competence(niveau)
    if not ok:
        if g.scodoc_dept:  # "usage web"
            flash(error_message, "error")
        return json_error(404, error_message)
    if g.scodoc_dept:  # "usage web"
        flash(f"""{ue.acronyme} associée au niveau "{niveau.libelle}" """)
    return {"status": 0}


@bp.route(
    "/desassoc_ue_niveau/<int:ue_id>",
    methods=["POST"],
)
@api_web_bp.route(
    "/desassoc_ue_niveau/<int:ue_id>",
    methods=["POST"],
)
@login_required
@scodoc
@permission_required(Permission.ScoChangeFormation)
@as_json
def desassoc_ue_niveau(ue_id: int):
    """Désassocie cette UE de son niveau de compétence
    (si elle n'est pas associée, ne fait rien)
    """
    query = UniteEns.query.filter_by(id=ue_id)
    if g.scodoc_dept:
        query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id)
    ue: UniteEns = query.first_or_404()
    ue.niveau_competence = None
    db.session.add(ue)
    db.session.commit()
    log(f"desassoc_ue_niveau: {ue}")
    if g.scodoc_dept:
        # "usage web"
        flash(f"UE {ue.acronyme} dé-associée")
    return {"status": 0}
