##############################################################################
#
# 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
#
##############################################################################

"""Accès aux emplois du temps

Génération des ics par enseignant à partir des ics par formation.
A utiliser quand les edt des enseignants ne sont pas fournis par l'extérieur.

Utilise la configuration globale pour extraire l'enseignant de chaque évènement.
Les évènements sans enseignant identifiable sont ignorés.
Tous les ics de `edt_ics_path`/*.ics sont lus.
L'edt de chaque enseignant est enregistré dans `edt_ics_user_path`/user_edt_id.ics

La construction des ics se fait en mémoire, les fichiers sont écrits à la fin.
"""
from collections import defaultdict
import time
import icalendar

from flask import flash

from app import log
from app.auth.models import User
from app.models import ScoDocSiteConfig
from app.scodoc import sco_edt_cal
from app.scodoc.sco_exceptions import ScoValueError
import sco_version


def _calendar_factory():
    "Create a new calendar"
    cal = icalendar.Calendar()
    cal.add(
        "prodid", f"-//{sco_version.SCONAME} {sco_version.SCOVERSION}//scodoc.org//"
    )
    cal.add("version", "2.0")
    return cal


def generate_ens_calendars():
    """Regénère les calendriers de tous les enseignants"""
    t0 = time.time()
    edt_ics_uid_field = ScoDocSiteConfig.get("edt_ics_uid_field")
    if not edt_ics_uid_field:
        log("generate_ens_calendars: no edt_ics_uid_field, aborting")
        return
    edt_ics_user_path = ScoDocSiteConfig.get("edt_ics_user_path")
    if not edt_ics_user_path:
        log("generate_ens_calendars: no edt_ics_user_path, aborting")
        return
    edt_ics_uid_pattern = sco_edt_cal.get_ics_uid_pattern()
    edt_by_uid = defaultdict(_calendar_factory)
    edt2user: dict[str, User | None] = {}  # construit au fur et à mesure (cache)
    nb_events = 0  # to log
    ics_filenames = sco_edt_cal.list_edt_calendars()
    for ics_filename in ics_filenames:
        try:
            _, calendar = sco_edt_cal.load_calendar(ics_filename)
        except ScoValueError:
            continue  # skip, ignoring errors
        if not calendar:
            continue

        events = [e for e in calendar.walk() if e.name == "VEVENT"]
        nb_events += len(events)
        ens: User | None = None
        for event in events:
            edt_ens = sco_edt_cal.extract_event_data(
                event, edt_ics_uid_field, edt_ics_uid_pattern, none_if_no_match=True
            )
            if edt_ens in edt2user:
                ens = edt2user[edt_ens]
            else:
                ens = User.query.filter_by(edt_id=edt_ens).first()
                edt2user[edt_ens] = ens
            if ens:  # si l'utilisateur est reconnu
                event.add("X-ScoDoc-user", ens.user_name)
                edt_by_uid[edt_ens].add_component(event)
    _write_user_calendars(edt_by_uid)
    log(
        f"""generate_ens_calendars: done in {(time.time()-t0):g}s, processed {
            nb_events} events from {len(ics_filenames)} calendars, for {len(edt_by_uid)} users"""
    )


def _write_user_calendars(edt_by_uid: defaultdict):
    """Write all ics files, one per user"""
    for edt_id, cal in edt_by_uid.items():
        if not len(cal):  # nb: empty cals are True
            continue  # safeguard, never generate empty cals
        filename = sco_edt_cal.get_ics_user_edt_filename(edt_id)
        if filename:
            log(f"generate_ens_calendars: writing {filename}")
            try:
                with open(filename, "wb") as f:
                    f.write(cal.to_ical())
            except PermissionError:
                log("_write_user_calendars: permission denied")
                flash("Erreur: permission écriture calendrier non accordée")
                return  # abort
            except FileNotFoundError:
                log("_write_user_calendars: No such file or directory")
                flash("Erreur: chemin incorrect pour écrire le calendrier")
                return  # abort