# -*- coding: utf-8 -*-
"""
PLANNING TEAM - les demandes de l'equipe.

Trois choses qu'un salarie peut demander depuis sa page :

  * un CONGE          -> devient une absence en CP une fois accepte ;
  * une ABSENCE       -> maladie, evenement familial... decomptee en heures ;
  * un ECHANGE        -> il propose d'echanger une journee avec un collegue.
                         Le collegue doit accepter AVANT que la demande arrive sur le
                         bureau de Jerome : trois etapes, comme dans la vraie vie.

Toute demande est confrontee aux deux regles (un pharmacien en permanence, trois
personnes au comptoir) DES SA CREATION : le salarie voit tout de suite si ce qu'il
demande cree un trou, et peut choisir d'autres dates de lui-meme.
"""

import datetime
import os
import sys

BASE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, BASE)

import base as bd            # noqa: E402
import calendrier as cal     # noqa: E402
import conges as cg          # noqa: E402
import reel                  # noqa: E402

TYPES = {
    'conge':   u'Demande de cong\xe9',
    'absence': u'Signalement d’absence',
    'echange': u'\xc9change de cr\xe9neau',
}

SCHEMA = """
CREATE TABLE IF NOT EXISTS demande (
    id              INTEGER PRIMARY KEY,
    type            TEXT NOT NULL,
    salarie_id      INTEGER NOT NULL REFERENCES salarie(id) ON DELETE CASCADE,
    date_debut      TEXT NOT NULL,
    date_fin        TEXT NOT NULL,
    motif           TEXT,          -- motif d'absence : cp, maladie, formation...
    commentaire     TEXT,          -- le mot du salarie
    collegue_id     INTEGER REFERENCES salarie(id) ON DELETE SET NULL,
    statut_collegue TEXT,          -- attente | accepte | refuse   (echanges)
    statut          TEXT NOT NULL DEFAULT 'attente',  -- attente | acceptee | refusee
    reponse         TEXT,          -- motif du refus, facultatif
    traite_par      TEXT,
    absence_id      INTEGER,
    cree_le         TEXT NOT NULL,
    traite_le       TEXT
);

CREATE INDEX IF NOT EXISTS idx_demande_statut ON demande (statut, cree_le);
"""


def creer_schema(cx):
    if cg.tables_completes(cx, ('demande',)):
        return
    cx.executescript(SCHEMA)
    cx.commit()


def _maintenant():
    return datetime.datetime.now().isoformat(timespec='seconds')


# ------------------------------------------------------------------- impact

def _alertes_periode(cx, debut, fin):
    """{ date : nombre de creneaux en alerte } sur une periode."""
    resultat = {}
    vues = set()
    jour = debut
    while jour <= fin:
        annee_iso, numero = cal.semaine_de(jour)
        if (annee_iso, numero) not in vues:
            vues.add((annee_iso, numero))
            bloc = reel.semaine(cx, annee_iso, numero)
            for i, jour_bloc in enumerate(bloc['jours']):
                n = sum(1 for p in bloc['presence'][i + 1]
                        if p['sous_effectif'] or p['manque_pharmacien'])
                if n:
                    resultat[jour_bloc['date']] = n
        jour += datetime.timedelta(1)
    return resultat


def impact(cx, salarie_id, date_debut, date_fin):
    """
    Ce que couterait cette absence si elle etait accordee : les creneaux qui
    passeraient en alerte, et qui pourrait couvrir.

    On simule vraiment : l'absence est inseree, on recalcule, puis on annule. Le
    resultat est donc exactement ce qui se produira, pas une approximation.
    """
    debut = datetime.date.fromisoformat(date_debut)
    fin = datetime.date.fromisoformat(date_fin)

    # Tous les schemas doivent exister AVANT l'insertion simulee : leur creation
    # validerait la transaction, et l'absence de simulation deviendrait definitive.
    cg.creer_schema(cx)
    reel.creer_schema(cx)
    creer_schema(cx)

    avant = _alertes_periode(cx, debut, fin)
    try:
        cx.execute(
            """INSERT INTO absence
                 (salarie_id, date_debut, date_fin, motif, decompte, statut,
                  origine, cree_le)
               VALUES (?, ?, ?, 'cp', 'cp', 'validee', 'simulation', ?)""",
            (salarie_id, debut.isoformat(), fin.isoformat(), _maintenant()))
        apres = _alertes_periode(cx, debut, fin)
        remplacants = _remplacants_periode(cx, debut, fin, avant, apres)
    finally:
        cx.rollback()

    nouveaux = {}
    for date, n in apres.items():
        ecart = n - avant.get(date, 0)
        if ecart > 0:
            nouveaux[date] = ecart

    return {
        'creneaux_en_alerte': sum(nouveaux.values()),
        'jours': [{'date': d, 'creneaux': n} for d, n in sorted(nouveaux.items())],
        'remplacants': remplacants,
    }


def _remplacants_periode(cx, debut, fin, avant, apres):
    """Les remplacants proposes pour les jours qui se degradent."""
    propositions, vues = [], set()
    jour = debut
    while jour <= fin:
        annee_iso, numero = cal.semaine_de(jour)
        if (annee_iso, numero) not in vues:
            vues.add((annee_iso, numero))
            bloc = reel.semaine(cx, annee_iso, numero)
            for r in bloc['remplacants']:
                if apres.get(r['date'], 0) > avant.get(r['date'], 0):
                    propositions.append(r)
        jour += datetime.timedelta(1)
    return propositions


# ------------------------------------------------------------------ demandes

def deposer(cx, type_demande, salarie_id, date_debut, date_fin, motif=None,
            commentaire=None, collegue_id=None):
    """Enregistre une demande, avec son impact calcule au moment du depot."""
    creer_schema(cx)
    if type_demande not in TYPES:
        raise ValueError(u'type de demande inconnu : %s' % type_demande)
    if type_demande == 'echange' and not collegue_id:
        raise ValueError(u'un \xe9change doit d\xe9signer un coll\xe8gue')

    debut = datetime.date.fromisoformat(date_debut)
    fin = datetime.date.fromisoformat(date_fin)
    if fin < debut:
        raise ValueError(u'la date de fin pr\xe9c\xe8de la date de d\xe9but')

    cur = cx.execute(
        """INSERT INTO demande
             (type, salarie_id, date_debut, date_fin, motif, commentaire,
              collegue_id, statut_collegue, statut, cree_le)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'attente', ?)""",
        (type_demande, salarie_id, debut.isoformat(), fin.isoformat(),
         motif or ('cp' if type_demande == 'conge' else None), commentaire,
         collegue_id, 'attente' if type_demande == 'echange' else None,
         _maintenant()))
    cx.commit()
    return cur.lastrowid


def repondre_collegue(cx, demande_id, accepte):
    """Le collegue accepte ou refuse un echange. Sans son accord, rien ne remonte."""
    d = une(cx, demande_id)
    if d is None or d['type'] != 'echange':
        raise ValueError(u'cette demande n’est pas un \xe9change')
    cx.execute('UPDATE demande SET statut_collegue = ? WHERE id = ?',
               ('accepte' if accepte else 'refuse', demande_id))
    if not accepte:
        cx.execute(
            """UPDATE demande SET statut = 'refusee', reponse = ?, traite_le = ?
                WHERE id = ?""",
            (u'refus\xe9 par le coll\xe8gue', _maintenant(), demande_id))
    cx.commit()
    return une(cx, demande_id)


def repondre(cx, demande_id, accepte, reponse=None, par=None):
    """
    Jerome ou Aurore tranche. Une demande acceptee devient immediatement une
    absence reelle, decomptee selon son motif.
    """
    d = une(cx, demande_id)
    if d is None:
        raise ValueError(u'demande introuvable')
    if d['statut'] != 'attente':
        raise ValueError(u'cette demande a d\xe9j\xe0 \xe9t\xe9 trait\xe9e')
    if d['type'] == 'echange' and d['statut_collegue'] != 'accepte':
        raise ValueError(u'le coll\xe8gue n’a pas encore accept\xe9 cet \xe9change')

    absence_id = None
    if accepte and d['type'] in ('conge', 'absence'):
        absence_id = cg.poser_absence(
            cx, d['salarie_id'], d['date_debut'], d['date_fin'],
            d['motif'] or 'cp',
            decompte='cp' if (d['motif'] or 'cp') == 'cp' else 'heures',
            libelle=d['commentaire'])
        if (d['motif'] or 'cp') != 'cp':
            heures = reel.heures_prevues(
                cx, d['salarie_id'],
                datetime.date.fromisoformat(d['date_debut']),
                datetime.date.fromisoformat(d['date_fin']))['heures']
            cx.execute('UPDATE absence SET heures = ? WHERE id = ?',
                       (heures, absence_id))

    cx.execute(
        """UPDATE demande
              SET statut = ?, reponse = ?, traite_par = ?, traite_le = ?, absence_id = ?
            WHERE id = ?""",
        ('acceptee' if accepte else 'refusee', reponse, par, _maintenant(),
         absence_id, demande_id))
    cx.commit()
    return une(cx, demande_id)


def supprimer(cx, demande_id):
    cx.execute('DELETE FROM demande WHERE id = ?', (demande_id,))
    cx.commit()


def une(cx, demande_id):
    l = cx.execute('SELECT * FROM demande WHERE id = ?', (demande_id,)).fetchone()
    return dict(l) if l else None


def liste(cx, statut=None, salarie_id=None):
    """Les demandes, les plus recentes d'abord, avec les noms deja resolus."""
    creer_schema(cx)
    conditions, params = ['1 = 1'], []
    if statut:
        conditions.append('d.statut = ?')
        params.append(statut)
    if salarie_id:
        conditions.append('(d.salarie_id = ? OR d.collegue_id = ?)')
        params.extend([salarie_id, salarie_id])

    lignes = cx.execute(
        """SELECT d.*,
                  s.prenom AS prenom, s.initiale AS initiale, s.couleur AS couleur,
                  c.prenom AS prenom_collegue
             FROM demande d
             JOIN salarie s ON s.id = d.salarie_id
             LEFT JOIN salarie c ON c.id = d.collegue_id
            WHERE %s
            ORDER BY d.statut = 'attente' DESC, d.cree_le DESC"""
        % ' AND '.join(conditions), params).fetchall()

    resultat = []
    for l in lignes:
        bloc = dict(l)
        bloc['libelle_type'] = TYPES.get(bloc['type'], bloc['type'])
        resultat.append(bloc)
    return resultat


def en_attente(cx):
    """Combien de demandes attendent une reponse - pour la pastille de l'onglet."""
    creer_schema(cx)
    return cx.execute(
        """SELECT COUNT(*) AS n FROM demande
            WHERE statut = 'attente'
              AND (type <> 'echange' OR statut_collegue = 'accepte')""").fetchone()['n']


if __name__ == '__main__':
    cx = bd.connexion()
    creer_schema(cx)

    sid = cx.execute("SELECT id FROM salarie WHERE initiale = 'Sa'").fetchone()['id']
    print(u'Impact d’un cong\xe9 de Sariaka du 07 au 12/09/2026 :')
    i = impact(cx, sid, '2026-09-07', '2026-09-12')
    print(u'  %d cr\xe9neau(x) passeraient en alerte' % i['creneaux_en_alerte'])
    for j in i['jours']:
        print(u'    %s : %d' % (j['date'], j['creneaux']))
    for r in i['remplacants']:
        noms = ', '.join(c['prenom'] for c in r['candidats'])
        print(u'    rempla\xe7ants le %s : %s' % (r['date'], noms or u'aucun'))

    print(u'\nDemandes en attente : %d' % en_attente(cx))
    cx.close()
