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

##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2024 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
#
##############################################################################

"""Génération du PV de jury en PDF (celui en format paysage avec l'ensemble des décisions)
"""
import io

import reportlab
from reportlab.lib.units import cm, mm
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.platypus import (
    Paragraph,
    Spacer,
    PageBreak,
    Table,
)
from reportlab.platypus.doctemplate import BaseDocTemplate
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib import styles
from reportlab.lib.colors import Color

from app.models import FormSemestre

from app.scodoc import codes_cursus
from app.scodoc.sco_exceptions import ScoPDFFormatError
from app.scodoc import sco_pv_dict
from app.scodoc import sco_pdf
from app.scodoc import sco_preferences
from app.scodoc.sco_pdf import SU
from app.scodoc.sco_pv_templates import PVTemplate, jury_titres
import sco_version


# ----------------------------------------------
def pvjury_pdf(
    formsemestre: FormSemestre,
    etudids: list[int],
    date_commission=None,
    date_jury=None,
    numero_arrete=None,
    code_vdi=None,
    show_title=False,
    pv_title=None,
    pv_title_session=None,
    with_paragraph_nom=False,
    anonymous=False,
) -> bytes:
    """Doc PDF récapitulant les décisions de jury
    (tableau en format paysage)
    """
    objects, a_diplome = _pvjury_pdf_type(
        formsemestre,
        etudids,
        only_diplome=False,
        date_commission=date_commission,
        numero_arrete=numero_arrete,
        code_vdi=code_vdi,
        date_jury=date_jury,
        show_title=show_title,
        pv_title_session=pv_title_session,
        pv_title=pv_title,
        with_paragraph_nom=with_paragraph_nom,
        anonymous=anonymous,
    )
    if not objects:
        return b""

    jury_de_diplome = formsemestre.est_terminal()

    # Si Jury de passage et qu'un étudiant valide le parcours
    #  (car il a validé antérieurement le dernier semestre)
    # alors on génère aussi un PV de diplome (à la suite dans le même doc PDF)
    if not jury_de_diplome and a_diplome:
        # au moins un etudiant a validé son diplome:
        objects.append(PageBreak())
        objects += _pvjury_pdf_type(
            formsemestre,
            etudids,
            only_diplome=True,
            date_commission=date_commission,
            date_jury=date_jury,
            numero_arrete=numero_arrete,
            code_vdi=code_vdi,
            show_title=show_title,
            pv_title_session=pv_title_session,
            pv_title=pv_title,
            with_paragraph_nom=with_paragraph_nom,
            anonymous=anonymous,
        )[0]

    # ----- Build PDF
    report = io.BytesIO()  # in-memory document, no disk file
    document = BaseDocTemplate(report)
    document.pagesize = landscape(A4)
    document.addPageTemplates(
        PVTemplate(
            document,
            author=f"{sco_version.SCONAME} {sco_version.SCOVERSION} (E. Viennet)",
            title=SU(f"PV du jury de {formsemestre.titre_num()}"),
            subject="PV jury",
            preferences=sco_preferences.SemPreferences(formsemestre.id),
        )
    )

    try:
        document.build(objects)
    except (ValueError, KeyError, reportlab.platypus.doctemplate.LayoutError) as exc:
        raise ScoPDFFormatError(str(exc)) from exc

    data = report.getvalue()
    return data


def _make_pv_styles(formsemestre: FormSemestre):
    style = reportlab.lib.styles.ParagraphStyle({})
    style.fontSize = 12
    style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre.id)
    style.leading = 18
    style.alignment = TA_JUSTIFY

    indent = 1 * cm
    style_bullet = reportlab.lib.styles.ParagraphStyle({})
    style_bullet.fontSize = 12
    style_bullet.fontName = sco_preferences.get_preference(
        "PV_FONTNAME", formsemestre.id
    )
    style_bullet.leading = 12
    style_bullet.alignment = TA_JUSTIFY
    style_bullet.firstLineIndent = 0
    style_bullet.leftIndent = indent
    style_bullet.bulletIndent = indent
    style_bullet.bulletFontName = "Times-Roman"
    style_bullet.bulletFontSize = 11
    style_bullet.spaceBefore = 5 * mm
    style_bullet.spaceAfter = 5 * mm
    return style, style_bullet


def _pvjury_pdf_type(
    formsemestre: FormSemestre,
    etudids: list[int],
    only_diplome=False,
    date_commission=None,
    date_jury=None,
    numero_arrete=None,
    code_vdi=None,
    show_title=False,
    pv_title=None,
    pv_title_session=None,
    anonymous=False,
    with_paragraph_nom=False,
) -> tuple[list, bool]:
    """Objets platypus PDF récapitulant les décisions de jury
    pour un type de jury (passage ou delivrance).
    Ramene: liste d'onj platypus, et un boolen indiquant si au moins un étudiant est diplômé.
    """
    from app.scodoc import sco_pv_forms
    from app.but import jury_but_pv

    a_diplome = False
    # Jury de diplome si sem. terminal OU que l'on demande seulement les diplomés
    diplome = formsemestre.est_terminal() or only_diplome
    titre_jury, _ = jury_titres(formsemestre, diplome)
    titre_diplome = pv_title or formsemestre.formation.titre_officiel
    objects = []

    style, style_bullet = _make_pv_styles(formsemestre)

    objects += [Spacer(0, 5 * mm)]
    objects += sco_pdf.make_paras(
        f"""
    <para align="center"><b>Procès-verbal de {titre_jury} du département {
        sco_preferences.get_preference("DeptName", formsemestre.id) or "(sans nom)"
    } - {pv_title_session} {
        formsemestre.annee_scolaire()} - {
        formsemestre.annee_scolaire()+1}</b></para>
    """,
        style,
    )

    objects += sco_pdf.make_paras(
        f"""<para align="center"><b><i>{titre_diplome}</i></b></para>""",
        style,
    )

    if show_title:
        objects += sco_pdf.make_paras(
            f"""<para align="center"><b>Semestre: {formsemestre.titre}</b></para>""",
            style,
        )
    if sco_preferences.get_preference("PV_TITLE_WITH_VDI", formsemestre.id):
        objects += sco_pdf.make_paras(
            f"""<para align="center">VDI et Code: {(code_vdi or "")}</para>""", style
        )

    if date_jury:
        objects += sco_pdf.make_paras(
            f"""<para align="center">Jury tenu le {date_jury}</para>""", style
        )

    try:
        objects += sco_pdf.make_paras(
            "<para>"
            + (sco_preferences.get_preference("PV_INTRO", formsemestre.id) or "")
            % {
                "Decnum": numero_arrete,
                "VDICode": code_vdi,
                "UnivName": sco_preferences.get_preference("UnivName", formsemestre.id),
                "Type": titre_jury,
                "Date": date_commission,  # deprecated
                "date_commission": date_commission,
            }
            + "</para>",
            style_bullet,
        )
    except KeyError as exc:
        raise ScoPDFFormatError(msg=f"balise inconnue: {exc.args[0]}") from exc

    objects += sco_pdf.make_paras(
        """<para>Le jury propose les décisions suivantes :</para>""", style
    )
    objects += [Spacer(0, 4 * mm)]

    if formsemestre.formation.is_apc():
        rows, titles = jury_but_pv.pvjury_table_but(
            formsemestre,
            etudids=etudids,
            line_sep="<br/>",
            only_diplome=only_diplome,
            anonymous=anonymous,
            with_paragraph_nom=with_paragraph_nom,
        )
        columns_ids = list(titles.keys())
        a_diplome = codes_cursus.ADM in [row.get("diplome") for row in rows]
    else:
        dpv = sco_pv_dict.dict_pvjury(formsemestre.id, etudids=etudids, with_prev=True)
        if not dpv:
            return [], False
        rows, titles, columns_ids = sco_pv_forms.pvjury_table(
            dpv,
            only_diplome=only_diplome,
            anonymous=anonymous,
            with_paragraph_nom=with_paragraph_nom,
        )
        a_diplome = True in (x["validation_parcours"] for x in dpv["decisions"])
    # convert to lists of tuples:
    columns_ids = ["etudid"] + columns_ids
    rows = [[line.get(x, "") for x in columns_ids] for line in rows]
    titles = [titles.get(x, "") for x in columns_ids]
    # Make a new cell style and put all cells in paragraphs
    cell_style = styles.ParagraphStyle({})
    cell_style.fontSize = sco_preferences.get_preference(
        "SCOLAR_FONT_SIZE", formsemestre.id
    )
    cell_style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre.id)
    cell_style.leading = 1.0 * sco_preferences.get_preference(
        "SCOLAR_FONT_SIZE", formsemestre.id
    )  # vertical space
    LINEWIDTH = 0.5
    table_style = [
        (
            "FONTNAME",
            (0, 0),
            (-1, 0),
            sco_preferences.get_preference("PV_FONTNAME", formsemestre.id),
        ),
        ("LINEBELOW", (0, 0), (-1, 0), LINEWIDTH, Color(0, 0, 0)),
        ("GRID", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
    ]
    titles = [f"<para><b>{x}</b></para>" for x in titles]

    def _format_pv_cell(x):
        """convert string to paragraph"""
        if isinstance(x, str):
            return Paragraph(SU(x), cell_style)
        else:
            return x

    widths_by_id = {
        "nom": 5 * cm,
        "cursus": 2.8 * cm,
        "ects": 1.4 * cm,
        "devenir": 1.8 * cm,
        "decision_but": 1.8 * cm,
    }

    table_cells = [[_format_pv_cell(x) for x in line[1:]] for line in ([titles] + rows)]
    widths = [widths_by_id.get(col_id) for col_id in columns_ids[1:]]

    objects.append(
        Table(table_cells, repeatRows=1, colWidths=widths, style=table_style)
    )

    # Signature du directeur
    objects += sco_pdf.make_paras(
        f"""<para spaceBefore="10mm" align="right">{
            sco_preferences.get_preference("DirectorName", formsemestre.id) or ""
        }, {
            sco_preferences.get_preference("DirectorTitle", formsemestre.id) or ""
        }</para>""",
        style,
    )

    # Légende des codes
    codes = list(codes_cursus.CODES_EXPL.keys())
    codes.sort()
    objects += sco_pdf.make_paras(
        """<para spaceBefore="15mm" fontSize="14">
    <b>Codes utilisés :</b></para>""",
        style,
    )
    L = []
    for code in codes:
        L.append((code, codes_cursus.CODES_EXPL[code]))
    TableStyle2 = [
        (
            "FONTNAME",
            (0, 0),
            (-1, 0),
            sco_preferences.get_preference("PV_FONTNAME", formsemestre.id),
        ),
        ("LINEBELOW", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
        ("LINEABOVE", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
        ("LINEBEFORE", (0, 0), (0, -1), LINEWIDTH, Color(0, 0, 0)),
        ("LINEAFTER", (-1, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
    ]
    objects.append(
        Table(
            [[Paragraph(SU(x), cell_style) for x in line] for line in L],
            colWidths=(2 * cm, None),
            style=TableStyle2,
        )
    )

    return objects, a_diplome