"""Routes — Événements (anniversaires + ad hoc).""" from datetime import date, timedelta from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.core.database import get_db from app.core.security import get_current_membre, require_executif from app.models.groupe import Groupe from app.models.membre import Membre from app.models.affectation import Affectation from app.models.poste import Poste from app.models.evenement import Evenement router = APIRouter(prefix="/evenements", tags=["Événements"]) def _anniversaire_sobriete(date_abstinence: date, annee: int) -> tuple[int, date]: try: anniv = date_abstinence.replace(year=annee) except ValueError: anniv = date(annee, 3, 1) return max(0, anniv.year - date_abstinence.year), anniv @router.get("/") async def evenements_a_venir( jours: int = 90, jours_avant: int = 365, membre=Depends(get_current_membre), db: AsyncSession = Depends(get_db), ): """Tous les événements : anniversaires et ad hoc.""" aujourdhui = date.today() debut = aujourdhui - timedelta(days=jours_avant) limite = aujourdhui + timedelta(days=jours) groupe = await db.get(Groupe, membre.groupe_id) district = groupe.district if groupe else "87-16" evenements = [] # ── 1. Anniversaires de sobriété du district ── result = await db.execute( select(Membre, Groupe.nom) .join(Groupe, Membre.groupe_id == Groupe.id) .where( Groupe.district == district, Membre.actif == True, Membre.date_abstinence.isnot(None), ) ) for m, groupe_nom in result.all(): for annee in range(debut.year, limite.year + 1): annees, anniv = _anniversaire_sobriete(m.date_abstinence, annee) if anniv < debut or anniv > limite: continue evenements.append({ "type": "anniversaire", "date": str(anniv), "titre": f"🎂 {m.prenom} — {annees} an{'s' if annees > 1 else ''}", "detail": f"Anniversaire de sobriété · {groupe_nom}", "lieu": None, "groupe_nom": groupe_nom, }) # ── 2. Événements ad hoc ── result = await db.execute( select(Evenement, Groupe.nom) .join(Groupe, Evenement.groupe_id == Groupe.id) .where( Groupe.district == district, Evenement.date_evenement >= debut, Evenement.date_evenement <= limite, ) ) for ev, groupe_nom in result.all(): evenements.append({ "type": "adhoc", "date": str(ev.date_evenement), "titre": ev.titre, "detail": ev.description, "lieu": ev.lieu, "groupe_nom": groupe_nom, }) # Trier par date evenements.sort(key=lambda e: e["date"]) return evenements # ── CRUD événements ad hoc ── @router.post("/") async def creer_evenement( titre: str, date_evenement: date, description: str = "", lieu: str = "", executif=Depends(require_executif), db: AsyncSession = Depends(get_db), ): """Créer un événement ad hoc (exécutif).""" ev = Evenement( groupe_id=executif.groupe_id, titre=titre.strip(), description=description.strip() or None, date_evenement=date_evenement, lieu=lieu.strip() or None, cree_par=executif.id, ) db.add(ev) await db.flush() return {"message": "Événement créé", "id": str(ev.id)} @router.delete("/{evenement_id}") async def supprimer_evenement( evenement_id: str, executif=Depends(require_executif), db: AsyncSession = Depends(get_db), ): result = await db.execute( select(Evenement).where( Evenement.id == evenement_id, Evenement.groupe_id == executif.groupe_id, ) ) ev = result.scalar_one_or_none() if not ev: raise HTTPException(status_code=404, detail="Événement introuvable") await db.delete(ev) await db.flush() return {"message": "Événement supprimé"}