#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
"""
Effectue la recréditation des impressions qui n'ont pas été effectuées
par l'imprimante, soit ne sont pas sorties correctement, et contiennent
des logs d'erreur.

Le script est fait pour éviter de recréditer plusieurs fois les mêmes
impressions, et croise dans l'historique de l'adhérent les impressions
(débits), et les créditations que ce script a lui même déjà effectuées.

Un mail est envoyé pour signaler les activités de crédits.
L'exécution se fait pas appel du script suivi de l'aid.

Auteurs : Gabriel Détraz <detraz@crans.org>
Pierre-Elliott Bécue <becue@crans.org>
avec contributions importantes de Daniel Stan, Valentin Samir et Lucas Serrano
"""

# Import des modules génériques
import argparse
import sys
import datetime
import re
from utils.sendmail import actually_sendmail

# Imports des scripts développés au Crans
import lc_ldap.shortcuts
import lc_ldap.filter2 as lfilter
import gestion.config as config
from pythondialog.dialog import Dialog
from gestion import mail

# On ouvre une connexion LDAP à la base de test une fois pour toute.
conn = lc_ldap.shortcuts.lc_ldap_admin()

# Des fonctions...
IMP_DONE_REGEX = re.compile(r".*debit (?P<montant>[^ ]*) Euros \[impression\((?P<jid>.*)\):.*/(?P<login>.*)/(?P<file>[^ ]*) .*\]")
IMP_DEJ_REMB_REGEX = re.compile(r".*credit (?P<montant>[^ ]*) Euros \[Impression ratee, jid=(?P<jid>.*), tache=(?P<fichier>.*)\]")

def do_remb(args):
    """Fonction appelant l'interface curses et listant les remboursements
    possibles.

    """

    rendu = []

    adh = conn.search(lfilter.human_to_ldap(args.filtre.decode(config.encoding.in_encoding)), mode="w")
    # conn.search peut retourner une liste vide.
    if not adh:
        return []

    with adh[0] as adh:
        liste_rembs = find_rembs(adh)
        while True:
            (ret, val) = dialog_remb(liste_rembs)
            if ret:
                adh.save()
                break
            else:
                selected = liste_rembs[int(val)]
                if confirm(selected):
                    adh.solde(float(selected["montant"]), u"Impression ratee, jid=%(jid)s, tache=%(file)s" % selected)
                    _ = liste_rembs.pop(int(val))
                    rendu.append(selected)
                else:
                    continue
    send_mail(rendu, adh)
    return

def find_rembs(adh):
    """Fonction qui calcule les entrées remboursables d'un adhérent

    """

    now = datetime.datetime.now()
    begin = now - datetime.timedelta(days=config.impression.delta_recredit_jours)
    liste_taches = list()
    liste_rembs = list()

    for ent in adh["historique"]:
        date = ent.get_datetime()
        if date > begin:
            match_entry = IMP_DONE_REGEX.match(unicode(ent))
            if match_entry is not None:
                liste_taches.append(match_entry.groupdict())
                continue
            match_entry = IMP_DEJ_REMB_REGEX.match(unicode(ent))
            if match_entry is not None:
                liste_rembs.append(match_entry.groupdict())
                continue

    # On génère la liste avant le return, pour ne pas la regénérer autant de fois qu'il
    # y a de tâches dans liste_taches
    jid_rembs = [remb["jid"] for remb in liste_rembs]
    return [tache for tache in liste_taches if tache['jid'] not in jid_rembs]

def dialog_remb(liste_rembs):
    """Crée une boîte de dialogue à partir de la liste des remboursements, pour
    en faire un.

    """
    if not liste_rembs:
        return (1, "")

    dialog = Dialog()
    choices = [("%s" % liste_rembs.index(dico), "Remb impression du fichier %(file)s (montant: %(montant)s, jid: %(jid)s)." % dico) for dico in liste_rembs]

    return dialog.menu("Quelle impression souhaitez-vous rembourser ?", width=0, height=0, menu_height=0, title="Remboursement", choices=choices, cancel_label="Quitter", backtitle=u"Remboursement d'impressions ratées.")

def confirm(dico):
    """Demande avec dialog si on doit confirmer le remboursement.

    """
    dialog = Dialog()
    return dialog.yesno("Confirmer le remboursement ?") == dialog.DIALOG_OK

def send_mail(liste_rendus, adh):
    """Si la liste des remboursements est non-vide, envoie un mail pour prévenir
    les imprimeurs.

    """
    if not liste_rendus:
        return

    montant = sum([float(dico["montant"]) for dico in liste_rendus])
    affs = [dico['file'] for dico in liste_rendus]
    login = unicode(adh["uid"][0])

#    print adh.get_mail()
    To = adh.get_mail()
    From = 'impression@crans.org'
    Cc = 'impression@crans.org'
    mailtxt = mail.generate('remboursement_impressions', {
        'To': To,
        'From': From,
        'adhname': login,
        'taches': u', '.join(affs),
        'montant': montant,
        'imprimeur': lc_ldap.shortcuts.current_user,
    })

#    print mailtxt.as_string()
    actually_sendmail(From, (To, Cc), mailtxt)

#Un bloc de test
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Script pour recréditer les adhérents.", add_help=False)
    parser.add_argument('-d', '--do-it', help="Sans cette option, le script ne fait rien (la méthode canonique de remboursement est via l'intranet", action="store_true")
    parser.add_argument('-h', '--help', help="Affiche ce message et quitte.",   action="store_true")
    parser.add_argument('filtre', type=str, nargs="?", help="Le filtre LDAP à utiliser")

    # Et on parse
    args = parser.parse_args()

    if args.help:
        parser.print_help()
        sys.exit(0)
    elif not args.do_it:
        print "Pour pouvoir utiliser ce script, merci de fournir l'option -d ou --do-it. La méthode usuelle pour recréditer une impression foirée est de passer par l'interface d'impression de l'Intranet. Allez d'abord voir là-bas si vous pouvez faire le recrédit, et ensuite utilisez ce script. Merci :)"
        sys.exit(1)
    else:
        if not args.filtre:
            parser.print_help()
            sys.exit(1)
        else:
            do_remb(args)

