# -*- mode: python -*-
# -*- coding: utf-8 -*-

##############################################################################
#
# ScoDoc
#
# Copyright (c) 1999 - 2022 Emmanuel Viennet.  All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#   Emmanuel Viennet      emmanuel.viennet@viennet.net
#
##############################################################################

"""
Module main: page d'accueil, avec liste des départements

Emmanuel Viennet, 2021
"""
import datetime
import io
import re

import flask
from flask import abort, flash, url_for, redirect, render_template, send_file
from flask import request
import flask_login
from flask_login.utils import login_required, current_user
from PIL import Image as PILImage

from werkzeug.exceptions import BadRequest, NotFound


from app import db
from app.auth.models import User
from app.forms.main import config_logos, config_main
from app.forms.main.create_dept import CreateDeptForm
from app.forms.main.config_apo import CodesDecisionsForm
from app import models
from app.models import Departement, Identite
from app.models import departements
from app.models import FormSemestre, FormSemestreInscription
from app.models import ScoDocSiteConfig
from app.scodoc import sco_codes_parcours, sco_logos
from app.scodoc import sco_find_etud
from app.scodoc import sco_utils as scu
from app.decorators import (
    admin_required,
    scodoc7func,
    scodoc,
)
from app.scodoc.sco_exceptions import AccessDenied
from app.scodoc.sco_permissions import Permission
from app.views import scodoc_bp as bp
import sco_version


@bp.route("/")
@bp.route("/ScoDoc")
@bp.route("/ScoDoc/index")
def index():
    "Page d'accueil: liste des départements"
    depts = Departement.query.filter_by().order_by(Departement.acronym).all()
    return render_template(
        "scodoc.html",
        title=sco_version.SCONAME,
        current_app=flask.current_app,
        depts=depts,
        Permission=Permission,
        scu=scu,
    )


# Renvoie les url /ScoDoc/RT/ vers /ScoDoc/RT/Scolarite
@bp.route("/ScoDoc/<scodoc_dept>/")
def index_dept(scodoc_dept):
    return redirect(url_for("scolar.index_html", scodoc_dept=scodoc_dept))


@bp.route("/ScoDoc/create_dept", methods=["GET", "POST"])
@admin_required
def create_dept():
    """Form création département"""
    form = CreateDeptForm()
    if request.method == "POST" and form.cancel.data:  # cancel button
        return redirect(url_for("scodoc.index"))
    if form.validate_on_submit():
        departements.create_dept(
            form.acronym.data,
            visible=form.visible.data,
            # description=form.description.data,
        )
        flash(f"Département {form.acronym.data} créé.")
        return redirect(url_for("scodoc.index"))
    return render_template(
        "create_dept.html",
        form=form,
        title="Création d'un nouveau département",
    )


@bp.route("/ScoDoc/toggle_dept_vis/<dept_id>", methods=["GET", "POST"])
@admin_required
def toggle_dept_vis(dept_id):
    """Cache ou rend visible un dept"""
    dept = Departement.query.get_or_404(dept_id)
    dept.visible = not dept.visible
    db.session.add(dept)
    db.session.commit()
    return redirect(url_for("scodoc.index"))


@bp.route("/ScoDoc/config_codes_decisions", methods=["GET", "POST"])
@admin_required
def config_codes_decisions():
    """Form config codes decisions"""
    form = CodesDecisionsForm()
    if request.method == "POST" and form.cancel.data:  # cancel button
        return redirect(url_for("scodoc.index"))
    if form.validate_on_submit():
        for code in models.config.CODES_SCODOC_TO_APO:
            ScoDocSiteConfig.set_code_apo(code, getattr(form, code).data)
        flash(f"Codes décisions enregistrés.")
        return redirect(url_for("scodoc.index"))
    elif request.method == "GET":
        for code in models.config.CODES_SCODOC_TO_APO:
            getattr(form, code).data = ScoDocSiteConfig.get_code_apo(code)
    return render_template(
        "config_codes_decisions.html",
        form=form,
        title="Configuration des codes de décisions",
    )


@bp.route("/ScoDoc/table_etud_in_accessible_depts", methods=["POST"])
@login_required
def table_etud_in_accessible_depts():
    """recherche étudiants sur plusieurs départements"""
    return sco_find_etud.table_etud_in_accessible_depts(expnom=request.form["expnom"])


# Fonction d'API accessible sans aucun authentification
@bp.route("/ScoDoc/get_etud_dept")
def get_etud_dept():
    """Returns the dept acronym (eg "GEII") of an etud (identified by etudid,
    code_nip ou code_ine in the request).
    Ancienne API: ramène la chaine brute, texte sans JSON ou XML.
    """
    if "etudid" in request.args:
        # zero ou une réponse:
        etuds = [Identite.query.get(request.args["etudid"])]
    elif "code_nip" in request.args:
        # il peut y avoir plusieurs réponses si l'étudiant est passé par plusieurs départements
        etuds = Identite.query.filter_by(code_nip=request.args["code_nip"]).all()
    elif "code_ine" in request.args:
        etuds = Identite.query.filter_by(code_ine=request.args["code_ine"]).all()
    else:
        raise BadRequest(
            "missing argument (expected one among: etudid, code_nip or code_ine)"
        )
    if not etuds:
        raise NotFound("student not found")
    elif len(etuds) == 1:
        last_etud = etuds[0]
    else:
        # inscriptions dans plusieurs departements: cherche la plus recente
        last_etud = None
        last_date = None
        for etud in etuds:
            inscriptions = FormSemestreInscription.query.filter_by(etudid=etud.id).all()
            for ins in inscriptions:
                date_fin = FormSemestre.query.get(ins.formsemestre_id).date_fin
                if (last_date is None) or date_fin > last_date:
                    last_date = date_fin
                    last_etud = etud
        if not last_etud:
            # est présent dans plusieurs semestres mais inscrit dans aucun !
            # le choix a peu d'importance...
            last_etud = etuds[-1]

    return Departement.query.get_or_404(last_etud.dept_id).acronym


# Bricolage pour le portail IUTV avec ScoDoc 7: (DEPRECATED: NE PAS UTILISER !)
@bp.route(
    "/ScoDoc/search_inscr_etud_by_nip", methods=["GET"]
)  # pour compat anciens clients PHP
@scodoc
@scodoc7func
def search_inscr_etud_by_nip(code_nip, format="json", __ac_name="", __ac_password=""):
    auth_ok = False
    user_name = __ac_name
    user_password = __ac_password
    if user_name and user_password:
        u = User.query.filter_by(user_name=user_name).first()
        if u and u.check_password(user_password):
            auth_ok = True
            flask_login.login_user(u)
    if not auth_ok:
        abort(403)
    else:
        return sco_find_etud.search_inscr_etud_by_nip(code_nip=code_nip, format=format)


@bp.route("/ScoDoc/about")
@bp.route("/ScoDoc/Scolarite/<scodoc_dept>/about")
@login_required
def about(scodoc_dept=None):
    "version info"
    return render_template(
        "about.html",
        version=scu.get_scodoc_version(),
        news=sco_version.SCONEWS,
        logo=scu.icontag("borgne_img"),
    )


# ---- CONFIGURATION

# Notes pour variables config: (valeurs par défaut des paramètres de département)
# Chaines simples
# SCOLAR_FONT = "Helvetica"
# SCOLAR_FONT_SIZE = 10
# SCOLAR_FONT_SIZE_FOOT = 6
# INSTITUTION_NAME = "<b>Institut Universitaire de Technologie - Université Georges Perec</b>"
# INSTITUTION_ADDRESS = "Web <b>www.sor.bonne.top</b> - 11, rue Simon Crubelier  - 75017 Paris"
# INSTITUTION_CITY = "Paris"
# Textareas:
# DEFAULT_PDF_FOOTER_TEMPLATE = "Edité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s sur %(server_url)s"

# Booléens
# always_require_ine

# Logos:
# LOGO_FOOTER*, LOGO_HEADER*


@bp.route("/ScoDoc/configuration", methods=["GET", "POST"])
@admin_required
def configuration():
    "Page de configuration globale"
    if not current_user.is_administrator():
        raise AccessDenied("invalid user (%s) must be SuperAdmin" % current_user)
    return config_main.configuration()


@bp.route("/ScoDoc/get_bonus_description/<bonus_name>", methods=["GET"])
def get_bonus_description(bonus_name: str):
    "description text/html du bonus"
    if bonus_name == "default":
        bonus_name = ""
    bonus_class = ScoDocSiteConfig.get_bonus_sport_class_from_name(bonus_name)
    text = bonus_class.__doc__
    if text:
        fields = re.split(r"\n\n", text, maxsplit=1)
        if len(fields) > 1:
            first_line, text = fields
        else:
            first_line, text = "", fields[0]
    else:
        text = ""
        first_line = "pas de fonction bonus configurée."

    return f"""<div class="bonus_description_head">{first_line}</div>
    <div>{text}</div>
    """


@bp.route("/ScoDoc/configure_logos", methods=["GET", "POST"])
@admin_required
def configure_logos():
    "Page de configuration des logos (globale)"
    if not current_user.is_administrator():
        raise AccessDenied("invalid user (%s) must be SuperAdmin" % current_user)
    return config_logos.config_logos()


SMALL_SIZE = (200, 200)


def _return_logo(name="header", dept_id="", small=False, strict: bool = True):
    # stockée dans /opt/scodoc-data/config/logos donc servie manuellement ici
    # from app.scodoc.sco_photos import _http_jpeg_file

    logo = sco_logos.find_logo(name, dept_id, strict).select()
    if logo is not None:
        suffix = logo.suffix
        if small:
            with PILImage.open(logo.filepath) as im:
                # on garde le même format (on pourrait plus simplement générer systématiquement du JPEG)
                fmt = {  # adapt suffix to be compliant with PIL save format
                    "PNG": "PNG",
                    "JPG": "JPEG",
                    "JPEG": "JPEG",
                }[suffix.upper()]
                if fmt == "JPEG":
                    im = im.convert("RGB")
                im.thumbnail(SMALL_SIZE)
                stream = io.BytesIO()
                im.save(stream, fmt)
                stream.seek(0)
                return send_file(stream, mimetype=f"image/{fmt}")
        else:
            # return _http_jpeg_file(logo.filepath)
            #  ... replaces ...
            return send_file(
                logo.filepath,
                mimetype=f"image/{suffix}",
                last_modified=datetime.datetime.now(),
            )
    else:
        abort(404)


#  small version (copy/paste from get_logo
@bp.route("/ScoDoc/logos/<name>/small", defaults={"dept_id": None})
@bp.route("/ScoDoc/<int:dept_id>/logos/<name>/small")
@admin_required
def get_logo_small(name: str, dept_id: int):
    strict = request.args.get("strict", "False")
    return _return_logo(
        name,
        dept_id=dept_id,
        small=True,
        strict=strict.upper() not in ["0", "FALSE"],
    )


@bp.route(
    "/ScoDoc/logos/<name>", defaults={"dept_id": None}
)  # if dept not specified, take global logo
@bp.route("/ScoDoc/<int:dept_id>/logos/<name>")
@admin_required
def get_logo(name: str, dept_id: int):
    strict = request.args.get("strict", "False")
    return _return_logo(
        name,
        dept_id=dept_id,
        small=False,
        strict=strict.upper() not in ["0", "FALSE"],
    )


# essais
# @bp.route("/testlog")
# def testlog():
#     import time
#     from flask import current_app
#     from app import log

#     log(f"testlog called: handlers={current_app.logger.handlers}")
#     current_app.logger.debug(f"testlog message DEBUG")
#     current_app.logger.info(f"testlog message INFO")
#     current_app.logger.warning(f"testlog message WARNING")
#     current_app.logger.error(f"testlog message ERROR")
#     current_app.logger.critical(f"testlog message CRITICAL")
#     raise SyntaxError("une erreur de syntaxe")
#     return "testlog completed at " + str(time.time())