"""
PN / Référentiel de compétences

Emmanuel Viennet, 2021
"""
from pathlib import Path
import re

from flask import flash, url_for
from flask import Markup
from flask import current_app, g, request
from flask.templating import render_template
from flask_json import as_json
from flask_login import current_user
from werkzeug.utils import redirect
from werkzeug.utils import secure_filename

from app import db
from app import log

from app.decorators import scodoc, permission_required
from app.models import Formation
from app.models.but_refcomp import ApcReferentielCompetences
from app.but.import_refcomp import orebut_import_refcomp
from app.but.forms.refcomp_forms import FormationRefCompForm, RefCompLoadForm
from app.scodoc.gen_tables import GenTable
from app.scodoc import sco_utils as scu
from app.scodoc.sco_exceptions import ScoFormatError, ScoValueError
from app.scodoc.sco_permissions import Permission
from app.views import notes_bp as bp
from app.views import ScoData


@bp.route("/referentiel/comp/get/<int:refcomp_id>")
@scodoc
@permission_required(Permission.ScoView)
@as_json
def refcomp(refcomp_id):
    """Le référentiel de compétences, en JSON."""
    ref: ApcReferentielCompetences = ApcReferentielCompetences.query.get_or_404(
        refcomp_id
    )
    return ref.to_dict()


@bp.route("/referentiel/comp/show/<int:refcomp_id>")
@scodoc
@permission_required(Permission.ScoView)
def refcomp_show(refcomp_id):
    """Affichage du référentiel de compétences."""
    referentiel_competence = ApcReferentielCompetences.query.get_or_404(refcomp_id)
    return render_template(
        "but/refcomp_show.j2",
        ref=referentiel_competence,
        title="Référentiel de compétences",
        data_source=url_for(
            "notes.refcomp",
            scodoc_dept=g.scodoc_dept,
            refcomp_id=refcomp_id,
        ),
        sco=ScoData(),
        table_niveaux_parcours=referentiel_competence.table_niveaux_parcours(),
    )


@bp.route("/referentiel/comp/delete/<int:refcomp_id>", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoChangeFormation)
def refcomp_delete(refcomp_id):
    """Suppression du référentiel de la base. Le fichier source n'est pas affecté."""
    ref = ApcReferentielCompetences.query.get_or_404(refcomp_id)
    db.session.delete(ref)
    db.session.commit()
    flash("référentiel de compétences supprimé")
    return redirect(url_for("notes.refcomp_table", scodoc_dept=g.scodoc_dept))


@bp.route("/referentiel/comp/table")
@scodoc
@permission_required(Permission.ScoView)
def refcomp_table():
    """Liste html des ref. comp. chargés dans ce département"""
    refs = ApcReferentielCompetences.query.filter_by(dept_id=g.scodoc_dept_id)
    columns_ids = ("type_titre", "specialite_long", "version", "json", "nb_formations")
    if current_user.has_permission(Permission.ScoChangeFormation):
        columns_ids = ("suppr",) + columns_ids
    suppr_icon = scu.icontag(
        "delete_small_img", border="0", alt="supprimer", title="Supprimer"
    )
    tab = GenTable(
        columns_ids=columns_ids,
        titles={
            "suppr": "",
            "type_titre": "Type",
            "specialite_long": "Spécialité",
            "version": "Version",
            "json": "Export",
            "nb_formations": "Formations",
        },
        rows=[
            {
                "type_titre": ref.type_titre,
                "specialite_long": ref.specialite_long,
                "_specialite_long_target": url_for(
                    "notes.refcomp_show", scodoc_dept=g.scodoc_dept, refcomp_id=ref.id
                ),
                "version": ref.get_version(),
                "json": "json",
                "_json_target": url_for(
                    "notes.refcomp", scodoc_dept=g.scodoc_dept, refcomp_id=ref.id
                ),
                "nb_formations": len(ref.formations),
                "suppr": suppr_icon,
                "_suppr_target": url_for(
                    "notes.refcomp_delete", scodoc_dept=g.scodoc_dept, refcomp_id=ref.id
                ),
            }
            for ref in refs
        ],
    )
    return render_template(
        "but/refcomp_table.j2",
        tab=tab,
        title="Référentiels de compétences",
        sco=ScoData(),
    )


@bp.route(
    "/referentiel/comp/assoc_formation/<int:formation_id>", methods=["GET", "POST"]
)
@scodoc
@permission_required(Permission.ScoChangeFormation)
def refcomp_assoc_formation(formation_id: int):
    """Formulaire association ref. compétence"""
    formation = Formation.query.get_or_404(formation_id)
    form = FormationRefCompForm()
    form.referentiel_competence.choices = [
        (r.id, f"{r.type_titre} {r.specialite_long} ({r.get_version()})")
        for r in ApcReferentielCompetences.query.filter_by(dept_id=g.scodoc_dept_id)
    ]
    if request.method == "POST" and form.cancel.data:  # cancel button
        return redirect(
            url_for(
                "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id
            )
        )
    if form.validate_on_submit():
        referentiel_competence_id = form.referentiel_competence.data
        assert (
            ApcReferentielCompetences.query.get(referentiel_competence_id) is not None
        )
        formation.referentiel_competence_id = referentiel_competence_id
        db.session.add(formation)
        db.session.commit()
        flash("nouveau référentiel de compétences associé")
        return redirect(
            url_for(
                "notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation_id
            )
        )
    return render_template(
        "but/refcomp_assoc.j2",
        form=form,
        referentiel_competence_id=formation.referentiel_competence_id,
        formation=formation,
        title="Association réf. compétences",
    )


@bp.route("/referentiel/comp/desassoc_formation/<int:formation_id>", methods=["GET"])
@scodoc
@permission_required(Permission.ScoChangeFormation)
def refcomp_desassoc_formation(formation_id: int):
    """Désassocie la formation de son ref. de compétence"""
    formation = Formation.query.get_or_404(formation_id)
    formation.refcomp_desassoc()
    return redirect(
        url_for("notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id)
    )


@bp.route(
    "/referentiel/comp/load", defaults={"formation_id": None}, methods=["GET", "POST"]
)
@bp.route("/referentiel/comp/load/<int:formation_id>", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.ScoChangeFormation)
def refcomp_load(formation_id=None):
    """Formulaire association ref. compétence"""
    if formation_id is not None:
        formation = Formation.query.get_or_404(formation_id)
    else:
        formation = None
    refs_distrib_files = sorted(
        list(
            (
                Path(current_app.config["SCODOC_DIR"])
                / "ressources/referentiels/but2022/competences"
            ).glob("*.xml")
        )
    )
    refs_distrib_dict = [{"id": 0, "specialite": "Aucun", "created": "", "serial": ""}]
    i = 1
    for filename in refs_distrib_files:
        m = re.match(r".*/but-([A-Za-z0-9_]+)-([0-9]+)-([0-9]+).xml", str(filename))
        if not m:
            log(f"refcomp_load: ignoring {filename} (invalid filename)")
        elif (
            ApcReferentielCompetences.query.filter_by(
                scodoc_orig_filename=Path(filename).name, dept_id=g.scodoc_dept_id
            ).count()
            == 0
        ):
            refs_distrib_dict.append(
                {
                    "id": i,
                    "specialite": m.group(1),
                    "created": m.group(2),
                    "serial": m.group(3),
                    "filename": str(filename),
                }
            )
            i += 1
        else:
            log(f"refcomp_load: ignoring {filename} (already loaded)")

    form = RefCompLoadForm()
    if current_app and not current_app.config["DEBUG"]:
        form.upload.render_kw = {"disabled": "disabled"}

    form.referentiel_standard.choices = [
        (r["id"], f"{r['specialite']} ({r['created']}-{r['serial']})")
        for r in refs_distrib_dict
    ]
    if form.validate_on_submit():
        if form.upload.data:
            f = form.upload.data
            filename = secure_filename(f.filename)
        elif form.referentiel_standard.data:
            try:
                filename = refs_distrib_dict[int(form.referentiel_standard.data)][
                    "filename"
                ]
            except (ValueError, IndexError) as exc:
                raise ScoValueError("choix invalide") from exc
            f = open(filename, encoding="utf-8")
        else:
            raise ScoValueError("choix invalide")
        try:
            xml_data = f.read()
            _ = orebut_import_refcomp(
                xml_data, dept_id=g.scodoc_dept_id, orig_filename=Path(filename).name
            )
        except TypeError as exc:
            raise ScoFormatError(
                f"fichier XML Orébut invalide (1): {exc.args}"
            ) from exc
        # peut aussi lever ScoFormatError

        flash(
            Markup(f"Référentiel <tt>{Path(filename).name}</tt> chargé."),
            category="info",
        )

        return redirect(
            url_for(
                "notes.refcomp_table",
                scodoc_dept=g.scodoc_dept,
            )
        )

    return render_template(
        "but/refcomp_load.j2",
        form=form,
        formation=formation,
        title="Chargement réf. compétences",
    )
