import { COOKIE_NAME } from "@shared/const";
import { getSessionCookieOptions } from "./_core/cookies";
import { systemRouter } from "./_core/systemRouter";
import { publicProcedure, protectedProcedure, router } from "./_core/trpc";
import { parseLocalDateTime } from "./dateUtils";
import { z } from "zod";
import * as db from "./db";
import * as analyticsDb from "./analyticsDb";
import { generateAnalyticsExcel, generateAnalyticsPDF } from "./analyticsExportService";
import { TRPCError } from "@trpc/server";
import { sendInscriptionConfirmation, sendGroupEmail, sendNotificationFormateurNouvelleInscription, sendNotificationFormateurAnnulation, sendAlerteCapaciteAtteinte, sendNotificationPlaceDisponible, sendRemerciementPostFormation } from "./emailService";
import { logNotification } from "./notificationLogsDb";
import { generateExcelExport, generatePDFExport, generateFeuillePresence } from "./exportService";
import { parseExcelFile, validateImportData, importToDatabase } from "./importExcel";

// Procédure admin uniquement
const adminProcedure = protectedProcedure.use(({ ctx, next }) => {
  if (ctx.user.role !== 'admin') {
    throw new TRPCError({ code: 'FORBIDDEN', message: 'Accès réservé aux administrateurs' });
  }
  return next({ ctx });
});

export const appRouter = router({
  system: systemRouter,
  auth: router({
    me: publicProcedure.query(opts => opts.ctx.user),
    logout: publicProcedure.mutation(({ ctx }) => {
      const cookieOptions = getSessionCookieOptions(ctx.req);
      ctx.res.clearCookie(COOKIE_NAME, { ...cookieOptions, maxAge: -1 });
      return {
        success: true,
      } as const;
    }),
  }),

  // ===== FORMATIONS =====
  formations: router({
    list: protectedProcedure.query(async ({ ctx }) => {
      // Si l'utilisateur est formateur, ne montrer que les formations pour lesquelles il a des séquences
      if (ctx.user.role === 'formateur' && ctx.user.formateurId) {
        return db.getFormationsByFormateur(ctx.user.formateurId);
      } else if (ctx.user.role === 'admin') {
        return db.getFormations();
      } else {
        throw new TRPCError({ code: 'FORBIDDEN', message: 'Accès non autorisé' });
      }
    }),

    getById: adminProcedure.input(z.object({ id: z.number() })).query(async ({ input }) => {
      return db.getFormationById(input.id);
    }),

    getByLien: publicProcedure.input(z.object({ lien: z.string() })).query(async ({ input }) => {
      return db.getFormationByLien(input.lien);
    }),

    create: adminProcedure.input(z.object({
      nom: z.string().min(1),
      description: z.string().optional(),
      lienUnique: z.string().min(1),
      actif: z.boolean().optional(),
    })).mutation(async ({ input }) => {
      await db.createFormation(input);
      return { success: true };
    }),

    update: adminProcedure.input(z.object({
      id: z.number(),
      nom: z.string().min(1).optional(),
      description: z.string().optional(),
      lienUnique: z.string().min(1).optional(),
      actif: z.boolean().optional(),
    })).mutation(async ({ input }) => {
      const { id, ...data } = input;
      await db.updateFormation(id, data);
      return { success: true };
    }),

    delete: adminProcedure.input(z.object({ id: z.number() })).mutation(async ({ input }) => {
      await db.deleteFormation(input.id);
      return { success: true };
    }),

    importExcel: adminProcedure.input(z.object({
      fileBase64: z.string(),
    })).mutation(async ({ input }) => {
      try {
        // Décoder le fichier base64
        const buffer = Buffer.from(input.fileBase64, 'base64');
        
        // Parser le fichier Excel
        const { formations, sequences } = parseExcelFile(buffer);
        
        // Valider les données
        const validation = validateImportData(formations, sequences);
        
        if (!validation.valid) {
          return {
            success: false,
            formationsCreated: 0,
            sequencesCreated: 0,
            errors: validation.errors,
            warnings: validation.warnings,
          };
        }
        
        // Importer dans la base de données
        const result = await importToDatabase(formations, sequences);
        
        return {
          ...result,
          warnings: [...validation.warnings, ...result.warnings],
        };
      } catch (error: any) {
        return {
          success: false,
          formationsCreated: 0,
          sequencesCreated: 0,
          errors: [`Erreur lors du traitement du fichier: ${error.message}`],
          warnings: [],
        };
      }
    }),

    previewExcel: adminProcedure.input(z.object({
      fileBase64: z.string(),
    })).mutation(async ({ input }) => {
      try {
        const buffer = Buffer.from(input.fileBase64, 'base64');
        const { formations, sequences } = parseExcelFile(buffer);
        const validation = validateImportData(formations, sequences);
        
        return {
          formations,
          sequences,
          validation,
        };
      } catch (error: any) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: `Erreur lors de la lecture du fichier: ${error.message}`,
        });
      }
    }),
  }),

  // ===== APPRENANTS =====
  apprenants: router({
    list: protectedProcedure.query(async ({ ctx }) => {
      // Si l'utilisateur est formateur, ne montrer que les apprenants inscrits à ses séquences
      if (ctx.user.role === 'formateur' && ctx.user.formateurId) {
        return db.getApprenantsByFormateur(ctx.user.formateurId);
      } else if (ctx.user.role === 'admin') {
        return db.getApprenants();
      } else {
        throw new TRPCError({ code: 'FORBIDDEN', message: 'Accès non autorisé' });
      }
    }),

    getById: adminProcedure.input(z.object({ id: z.number() })).query(async ({ input }) => {
      return db.getApprenantById(input.id);
    }),

    getByEmail: publicProcedure.input(z.object({ email: z.string() })).query(async ({ input }) => {
      return db.getApprenantByEmail(input.email);
    }),

    create: adminProcedure.input(z.object({
      nom: z.string().min(1),
      prenom: z.string().min(1),
      email: z.string().email(),
      codeEtablissement: z.string().min(1),
      fonction: z.enum(["directeur", "chef_service", "autre"]),
    })).mutation(async ({ input }) => {
      await db.createApprenant(input);
      return { success: true };
    }),

    // Procédure publique pour créer un apprenant lors de l'inscription
    createPublic: publicProcedure.input(z.object({
      nom: z.string().min(1),
      prenom: z.string().min(1),
      email: z.string().email(),
      codeEtablissement: z.string().min(1),
      fonction: z.enum(["directeur", "chef_service", "autre"]),
    })).mutation(async ({ input }) => {
      await db.createApprenant(input);
      return { success: true };
    }),

    update: adminProcedure.input(z.object({
      id: z.number(),
      nom: z.string().min(1).optional(),
      prenom: z.string().min(1).optional(),
      email: z.string().email().optional(),
      codeEtablissement: z.string().min(1).optional(),
      fonction: z.enum(["directeur", "chef_service", "autre"]).optional(),
    })).mutation(async ({ input }) => {
      const { id, ...data } = input;
      await db.updateApprenant(id, data);
      return { success: true };
    }),

    delete: adminProcedure.input(z.object({ id: z.number() })).mutation(async ({ input }) => {
      await db.deleteApprenant(input.id);
      return { success: true };
    }),

    getInscriptions: adminProcedure.input(z.object({ id: z.number() })).query(async ({ input }) => {
      const results = await db.getInscriptionsByApprenant(input.id);
      // Récupérer les informations de formation et séquence pour chaque inscription
      const inscriptionsAvecDetails = await Promise.all(
        results.map(async (result: any) => {
          const insc = result.inscription;
          if (!insc) return null;
          const sequence = await db.getSequenceById(insc.sequenceId);
          if (!sequence) return null;
          const formation = await db.getFormationById(sequence.formationId);
          const dates = await db.getDatesBySequence(insc.sequenceId);
          return {
            ...insc,
            sequence: { ...sequence, dates },
            formation,
          };
        })
      );
      return inscriptionsAvecDetails.filter(i => i !== null);
    }),
  }),

  // ===== FORMATEURS =====
  formateurs: router({
    list: adminProcedure.query(async () => {
      return db.getFormateurs();
    }),

    // Tableau de bord formateur
    dashboardStats: protectedProcedure.query(async ({ ctx }) => {
      if (ctx.user.role !== 'formateur' || !ctx.user.formateurId) {
        throw new TRPCError({ code: 'FORBIDDEN', message: 'Accès réservé aux formateurs' });
      }
      const formateurDb = await import('./formateurDb');
      return formateurDb.getFormateurDashboardStats(ctx.user.formateurId);
    }),

    prochainesSequences: protectedProcedure
      .input(z.object({ limit: z.number().optional().default(10) }))
      .query(async ({ ctx, input }) => {
        if (ctx.user.role !== 'formateur' || !ctx.user.formateurId) {
          throw new TRPCError({ code: 'FORBIDDEN', message: 'Accès réservé aux formateurs' });
        }
        const formateurDb = await import('./formateurDb');
        return formateurDb.getFormateurProchainesSequences(ctx.user.formateurId, input.limit);
      }),

    getById: adminProcedure.input(z.object({ id: z.number() })).query(async ({ input }) => {
      return db.getFormateurById(input.id);
    }),

    create: adminProcedure.input(z.object({
      nom: z.string().min(1),
      email: z.string().email().optional(),
    })).mutation(async ({ input }) => {
      await db.createFormateur(input);
      return { success: true };
    }),

    update: adminProcedure.input(z.object({
      id: z.number(),
      nom: z.string().min(1).optional(),
      email: z.string().email().nullable().optional(),
    })).mutation(async ({ input }) => {
      const { id, ...data } = input;
      await db.updateFormateur(id, data);
      return { success: true };
    }),

    delete: adminProcedure.input(z.object({ id: z.number() })).mutation(async ({ input }) => {
      await db.deleteFormateur(input.id);
      return { success: true };
    }),
  }),

  // ===== SÉQUENCES =====
  sequences: router({
    list: protectedProcedure.query(async ({ ctx }) => {
      let seqs;
      
      // Si l'utilisateur est formateur, ne montrer que ses séquences
      if (ctx.user.role === 'formateur' && ctx.user.formateurId) {
        seqs = await db.getSequencesByFormateur(ctx.user.formateurId);
      } else if (ctx.user.role === 'admin') {
        seqs = await db.getSequencesWithFormation();
      } else {
        throw new TRPCError({ code: 'FORBIDDEN', message: 'Accès non autorisé' });
      }
      // Récupérer les dates et le nombre d'inscrits pour chaque séquence
      const sequencesAvecDates = await Promise.all(
        seqs.map(async (seq) => {
          const dates = await db.getDatesBySequence(seq.id);
          const nbInscrits = await db.countInscriptionsBySequence(seq.id, 'confirmee');
          
          // Récupérer les rappels associés à chaque date
          const datesAvecRappels = await Promise.all(
            dates.map(async (date) => {
              const rappels = await db.getRappelsByDateFormation(date.id);
              return { ...date, rappels };
            })
          );
          
          return { ...seq, dates: datesAvecRappels, nbInscrits };
        })
      );
      return sequencesAvecDates;
    }),

    listByFormation: publicProcedure.input(z.object({ formationId: z.number() })).query(async ({ input }) => {
      const seqs = await db.getSequencesByFormation(input.formationId);
      // Récupérer les dates et le nombre d'inscrits pour chaque séquence
      const sequencesAvecDates = await Promise.all(
        seqs.map(async (seq) => {
          const dates = await db.getDatesBySequence(seq.id);
          const nbInscrits = await db.countInscriptionsBySequence(seq.id, 'confirmee');
          return { ...seq, dates, nbInscrits };
        })
      );
      return sequencesAvecDates;
    }),

    getById: publicProcedure.input(z.object({ id: z.number() })).query(async ({ input }) => {
      const seq = await db.getSequenceById(input.id);
      if (!seq) return null;
      const dates = await db.getDatesBySequence(input.id);
      const nbInscrits = await db.countInscriptionsBySequence(input.id, 'confirmee');
      return { ...seq, dates, nbInscrits };
    }),

    create: adminProcedure.input(z.object({
      formationId: z.number(),
      nom: z.string().min(1),
      lieu: z.string().min(1),
      publicCible: z.enum(["directeur", "chef_service", "tous", "autre"]),
      formateurId: z.number().nullable().optional(),
      capaciteMax: z.number().default(12),
      dateBlocage: z.string(),
      statut: z.enum(["ouverte", "bloquee", "terminee"]).optional(),
      dates: z.array(z.object({
        dateDebut: z.string(),
        dateFin: z.string(),
        ordre: z.number(),
      })).min(1).max(4),
    })).mutation(async ({ input }) => {
      const { dates, ...sequenceData } = input;
      
      // Créer la séquence
      const result = await db.createSequence({
        ...sequenceData,
        dateBlocage: parseLocalDateTime(sequenceData.dateBlocage),
      });
      
      // Récupérer l'ID de la séquence créée
      const sequenceId = Number(result[0].insertId);
      
      // Créer les dates de formation
      for (const date of dates) {
        await db.createDateFormation({
          sequenceId,
          dateDebut: parseLocalDateTime(date.dateDebut),
          dateFin: parseLocalDateTime(date.dateFin),
          ordre: date.ordre,
        });
      }
      
      return { success: true, sequenceId };
    }),

    update: adminProcedure.input(z.object({
      id: z.number(),
      formationId: z.number(),
      nom: z.string().min(1),
      lieu: z.string().min(1),
      publicCible: z.enum(["directeur", "chef_service", "tous", "autre"]),
      formateurId: z.number().nullable().optional(),
      capaciteMax: z.number(),
      dateBlocage: z.string(),
      statut: z.enum(["ouverte", "bloquee", "terminee"]),
      dates: z.array(z.object({
        dateDebut: z.string(),
        dateFin: z.string(),
        ordre: z.number(),
      })).min(1).max(4),
    })).mutation(async ({ input }) => {
      const { id, dates, ...sequenceData } = input;
      
      console.log('[UPDATE SEQUENCE] Input reçu:', JSON.stringify(input, null, 2));
      
      // Mettre à jour la séquence
      const updateData: any = {
        ...sequenceData,
        dateBlocage: parseLocalDateTime(sequenceData.dateBlocage),
      };
      
      console.log('[UPDATE SEQUENCE] UpdateData:', JSON.stringify(updateData, null, 2));
      await db.updateSequence(id, updateData);
      console.log('[UPDATE SEQUENCE] Séquence mise à jour avec succès');
      
      // Supprimer les anciennes dates
      await db.deleteDatesBySequence(id);
      console.log('[UPDATE SEQUENCE] Anciennes dates supprimées');
      
      // Créer les nouvelles dates
      for (const date of dates) {
        console.log('[UPDATE SEQUENCE] Création date:', date);
        await db.createDateFormation({
          sequenceId: id,
          dateDebut: parseLocalDateTime(date.dateDebut),
          dateFin: parseLocalDateTime(date.dateFin),
          ordre: date.ordre,
        });
      }
      console.log('[UPDATE SEQUENCE] Toutes les dates ont été créées');
      
      return { success: true };
    }),

    delete: adminProcedure.input(z.object({ id: z.number() })).mutation(async ({ input }) => {
      // Supprimer d'abord les dates associées
      await db.deleteDatesBySequence(input.id);
      // Puis supprimer la séquence
      await db.deleteSequence(input.id);
      return { success: true };
    }),
  }),

  // ===== INSCRIPTIONS =====
  inscriptions: router({
    // Liste des inscriptions avec dates de formation (pour formateurs)
    listWithDates: protectedProcedure
      .input(z.object({ sequenceId: z.number() }))
      .query(async ({ input }) => {
        const { getInscriptionsWithDates } = await import("./inscriptionWithDates");
        return getInscriptionsWithDates(input.sequenceId);
      }),

    listAll: adminProcedure.query(async () => {
      return db.getAllInscriptions();
    }),

    listBySequence: adminProcedure.input(z.object({ sequenceId: z.number() })).query(async ({ input }) => {
      return db.getInscriptionsBySequence(input.sequenceId);
    }),

    listByApprenant: publicProcedure.input(z.object({ apprenantId: z.number() })).query(async ({ input }) => {
      return db.getInscriptionsByApprenant(input.apprenantId);
    }),

    listByApprenantWithDates: publicProcedure
      .input(z.object({ apprenantId: z.number() }))
      .query(async ({ input }) => {
        const { getInscriptionsByApprenantWithDates } = await import("./inscriptionWithDates");
        return getInscriptionsByApprenantWithDates(input.apprenantId);
      }),

    checkExisting: publicProcedure.input(z.object({
      apprenantId: z.number(),
      sequenceId: z.number(),
    })).query(async ({ input }) => {
      return db.checkExistingInscription(input.apprenantId, input.sequenceId);
    }),

    inscrire: publicProcedure.input(z.object({
      apprenantId: z.number(),
      sequenceId: z.number(),
    })).mutation(async ({ input }) => {
      // Vérifier si la séquence existe
      const sequence = await db.getSequenceById(input.sequenceId);
      if (!sequence) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'Séquence introuvable' });
      }

      // Vérifier si la séquence est bloquée
      const now = new Date();
      if (sequence.statut === 'bloquee' || now >= new Date(sequence.dateBlocage)) {
        throw new TRPCError({ 
          code: 'FORBIDDEN', 
          message: 'Les inscriptions sont fermées pour cette séquence (J-15 dépassé)' 
        });
      }

      // Vérifier si l'apprenant a déjà une inscription
      const existing = await db.checkExistingInscription(input.apprenantId, input.sequenceId);
      if (existing) {
        throw new TRPCError({ 
          code: 'CONFLICT', 
          message: 'Vous êtes déjà inscrit à cette séquence' 
        });
      }

      // Vérifier la capacité
      const nbInscrits = await db.countInscriptionsBySequence(input.sequenceId, 'confirmee');
      const statut = nbInscrits >= sequence.capaciteMax ? 'liste_attente' : 'confirmee';

      await db.createInscription({
        apprenantId: input.apprenantId,
        sequenceId: input.sequenceId,
        statut,
      });

      // Envoyer l'email de confirmation avec invitations Outlook pour toutes les dates
      const inscriptionSequence = await db.getSequenceById(input.sequenceId);
      const inscriptionApprenant = await db.getApprenantById(input.apprenantId);
      const inscriptionFormation = inscriptionSequence ? await db.getFormationById(inscriptionSequence.formationId) : null;
      const dates = inscriptionSequence ? await db.getDatesBySequence(inscriptionSequence.id) : [];
      
      if (inscriptionSequence && inscriptionApprenant && inscriptionFormation && dates.length > 0) {
        // Utiliser la première date pour l'email principal
        const premiereDate = dates[0];
        
        await sendInscriptionConfirmation({
          apprenantEmail: inscriptionApprenant.email,
          apprenantNom: inscriptionApprenant.nom,
          apprenantPrenom: inscriptionApprenant.prenom,
          apprenantFonction: inscriptionApprenant.fonction,
          formationNom: inscriptionFormation.nom,
          sequenceNom: inscriptionSequence.nom,
          dates: dates.map(d => ({
            dateDebut: new Date(d.dateDebut),
            dateFin: new Date(d.dateFin),
            ordre: d.ordre,
          })),
          lieu: inscriptionSequence.lieu,
          statut,
        });

        // Notification au formateur si présent
        if (inscriptionSequence.formateurId) {
          const formateur = await db.getFormateurById(inscriptionSequence.formateurId);
          if (formateur && formateur.email) {
            const nbInscritsActuel = await db.countInscriptionsBySequence(input.sequenceId, 'confirmee');
            try {
              await sendNotificationFormateurNouvelleInscription({
                formateurEmail: formateur.email,
                formateurNom: formateur.nom,
                apprenantNom: inscriptionApprenant.nom,
                apprenantPrenom: inscriptionApprenant.prenom,
                apprenantFonction: inscriptionApprenant.fonction,
                apprenantEtablissement: inscriptionApprenant.codeEtablissement,
                formationNom: inscriptionFormation.nom,
                sequenceNom: inscriptionSequence.nom,
                nbInscrits: nbInscritsActuel,
                capaciteMax: inscriptionSequence.capaciteMax,
                dates: dates.map(d => ({
                  dateDebut: new Date(d.dateDebut),
                  dateFin: new Date(d.dateFin),
                  ordre: d.ordre,
                })),
              });
              // Logger la notification
              await logNotification({
                type: "notification_formateur_inscription",
                sequenceId: input.sequenceId,
                apprenantId: inscriptionApprenant.id,
                formateurId: formateur.id,
                emailDestinataire: formateur.email,
                sujet: `Nouvelle inscription - ${inscriptionFormation.nom}`,
                statut: "success",
              });
              console.log(`[Notification] Email envoyé au formateur ${formateur.email} pour nouvelle inscription`);
            } catch (e: any) {
              // Logger l'échec
              await logNotification({
                type: "notification_formateur_inscription",
                sequenceId: input.sequenceId,
                apprenantId: inscriptionApprenant.id,
                formateurId: formateur.id,
                emailDestinataire: formateur.email,
                sujet: `Nouvelle inscription - ${inscriptionFormation.nom}`,
                statut: "failed",
                messageErreur: e.message,
              });
              console.error(`[Notification] Erreur envoi email formateur:`, e);
            }
          }
        }

        // Alerte capacité atteinte pour les admins ET le formateur
        const nbInscritsApres = await db.countInscriptionsBySequence(input.sequenceId, 'confirmee');
        if (nbInscritsApres >= inscriptionSequence.capaciteMax) {
          try {
            const admins = await db.getAdminUsers();
            const adminEmails = admins.filter(a => a.email).map(a => a.email!);
            if (adminEmails.length > 0) {
              const nbListeAttente = await db.countInscriptionsBySequence(input.sequenceId, 'liste_attente');
              let formateurNom: string | undefined;
              if (inscriptionSequence.formateurId) {
                const formateur = await db.getFormateurById(inscriptionSequence.formateurId);
                formateurNom = formateur?.nom;
              }
              await sendAlerteCapaciteAtteinte({
                adminEmails,
                formationNom: inscriptionFormation.nom,
                sequenceNom: inscriptionSequence.nom,
                capaciteMax: inscriptionSequence.capaciteMax,
                nbInscrits: nbInscritsApres,
                nbListeAttente,
                formateurNom,
                dates: dates.map(d => ({
                  dateDebut: new Date(d.dateDebut),
                  dateFin: new Date(d.dateFin),
                  ordre: d.ordre,
                })),
              });
              console.log(`[Notification] Alerte capacité atteinte envoyée à ${adminEmails.length} admin(s)`);
            }
            
            // Notification au formateur pour capacité atteinte
            if (inscriptionSequence.formateurId) {
              const formateur = await db.getFormateurById(inscriptionSequence.formateurId);
              if (formateur && formateur.email) {
                const { sendNotificationFormateurCapaciteAtteinte } = await import('./emailService');
                await sendNotificationFormateurCapaciteAtteinte({
                  formateurEmail: formateur.email,
                  formateurNom: formateur.nom,
                  formationNom: inscriptionFormation.nom,
                  sequenceNom: inscriptionSequence.nom,
                  capaciteMax: inscriptionSequence.capaciteMax,
                  nbInscrits: nbInscritsApres,
                  dates: dates.map(d => ({
                    dateDebut: new Date(d.dateDebut),
                    dateFin: new Date(d.dateFin),
                    ordre: d.ordre,
                  })),
                });
                console.log(`[Notification] Alerte capacité atteinte envoyée au formateur ${formateur.email}`);
              }
            }
          } catch (e) {
            console.error(`[Notification] Erreur envoi alerte capacité:`, e);
          }
        }
      }

      return { success: true, statut };
    }),

    desinscrire: publicProcedure.input(z.object({
      apprenantId: z.number(),
      sequenceId: z.number(),
    })).mutation(async ({ input }) => {
      // Vérifier si la séquence existe
      const sequence = await db.getSequenceById(input.sequenceId);
      if (!sequence) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'Séquence introuvable' });
      }

      // Vérifier si la séquence est bloquée
      const now = new Date();
      if (sequence.statut === 'bloquee' || now >= new Date(sequence.dateBlocage)) {
        throw new TRPCError({ 
          code: 'FORBIDDEN', 
          message: 'Les désinscriptions sont fermées pour cette séquence (J-15 dépassé)' 
        });
      }

      // Trouver l'inscription
      const inscription = await db.checkExistingInscription(input.apprenantId, input.sequenceId);
      if (!inscription) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'Inscription introuvable' });
      }

      // Récupérer les infos avant la mise à jour pour les notifications
      const apprenant = await db.getApprenantById(input.apprenantId);
      const formation = await db.getFormationById(sequence.formationId);

      await db.updateInscription(inscription.id, { statut: 'annulee' });

      // Notifications après désinscription
      if (apprenant && formation) {
        // Notification au formateur
        if (sequence.formateurId) {
          const formateur = await db.getFormateurById(sequence.formateurId);
          if (formateur && formateur.email) {
            const nbInscritsApres = await db.countInscriptionsBySequence(input.sequenceId, 'confirmee');
            try {
              await sendNotificationFormateurAnnulation({
                formateurEmail: formateur.email,
                formateurNom: formateur.nom,
                apprenantNom: apprenant.nom,
                apprenantPrenom: apprenant.prenom,
                apprenantFonction: apprenant.fonction || '',
                formationNom: formation.nom,
                sequenceNom: sequence.nom,
                nbInscrits: nbInscritsApres,
                capaciteMax: sequence.capaciteMax,
              });
              // Logger la notification
              await logNotification({
                type: "notification_formateur_annulation",
                sequenceId: input.sequenceId,
                apprenantId: apprenant.id,
                formateurId: formateur.id,
                emailDestinataire: formateur.email,
                sujet: `Annulation d'inscription - ${formation.nom}`,
                statut: "success",
              });
              console.log(`[Notification] Email d'annulation envoyé au formateur ${formateur.email}`);
            } catch (e: any) {
              // Logger l'échec
              await logNotification({
                type: "notification_formateur_annulation",
                sequenceId: input.sequenceId,
                apprenantId: apprenant.id,
                formateurId: formateur.id,
                emailDestinataire: formateur.email,
                sujet: `Annulation d'inscription - ${formation.nom}`,
                statut: "failed",
                messageErreur: e.message,
              });
              console.error(`[Notification] Erreur envoi email formateur:`, e);
            }
          }
        }

        // Notifier le premier en liste d'attente qu'une place s'est libérée
        const inscriptionsListeAttente = await db.getInscriptionsBySequence(input.sequenceId);
        const premierEnAttente = inscriptionsListeAttente
          .filter(i => i.inscription.statut === 'liste_attente')
          .sort((a, b) => new Date(a.inscription.dateInscription).getTime() - new Date(b.inscription.dateInscription).getTime())[0];
        
        if (premierEnAttente && premierEnAttente.apprenant) {
          try {
            await sendNotificationPlaceDisponible({
              apprenantEmail: premierEnAttente.apprenant.email,
              apprenantNom: premierEnAttente.apprenant.nom,
              apprenantPrenom: premierEnAttente.apprenant.prenom,
              apprenantFonction: premierEnAttente.apprenant.fonction,
              formationNom: formation.nom,
              sequenceNom: sequence.nom,
              positionListeAttente: 1,
              delaiReponse: 48,
            });
            // Logger la notification
            await logNotification({
              type: "notification_liste_attente",
              sequenceId: input.sequenceId,
              apprenantId: premierEnAttente.apprenant.id,
              emailDestinataire: premierEnAttente.apprenant.email,
              sujet: `Place disponible - ${formation.nom}`,
              statut: "success",
            });
            console.log(`[Notification] Email place disponible envoyé à ${premierEnAttente.apprenant.email}`);
          } catch (e: any) {
            // Logger l'échec
            await logNotification({
              type: "notification_liste_attente",
              sequenceId: input.sequenceId,
              apprenantId: premierEnAttente.apprenant.id,
              emailDestinataire: premierEnAttente.apprenant.email,
              sujet: `Place disponible - ${formation.nom}`,
              statut: "failed",
              messageErreur: e.message,
            });
            console.error(`[Notification] Erreur envoi email liste d'attente:`, e);
          }
        }
      }

      return { success: true };
    }),

    updateStatut: adminProcedure.input(z.object({
      id: z.number(),
      statut: z.enum(['confirmee', 'liste_attente', 'annulee']),
    })).mutation(async ({ input }) => {
      // Récupérer l'inscription avant modification
      const inscriptionAvant = await db.getInscriptionById(input.id);
      const ancienStatut = inscriptionAvant?.statut;
      
      await db.updateInscription(input.id, { statut: input.statut });
      
      // Si passage à annulée, notifier le formateur
      if (input.statut === 'annulee' && ancienStatut !== 'annulee' && inscriptionAvant) {
        const sequence = await db.getSequenceById(inscriptionAvant.sequenceId);
        const apprenant = await db.getApprenantById(inscriptionAvant.apprenantId);
        
        if (sequence && apprenant && sequence.formateurId) {
          const formateur = await db.getFormateurById(sequence.formateurId);
          const formation = await db.getFormationById(sequence.formationId);
          
          if (formateur && formateur.email && formation) {
            const nbInscritsApres = await db.countInscriptionsBySequence(sequence.id, 'confirmee');
            
            try {
              await sendNotificationFormateurAnnulation({
                formateurEmail: formateur.email,
                formateurNom: formateur.nom,
                apprenantNom: apprenant.nom,
                apprenantPrenom: apprenant.prenom,
                apprenantFonction: apprenant.fonction || '',
                formationNom: formation.nom,
                sequenceNom: sequence.nom,
                nbInscrits: nbInscritsApres,
                capaciteMax: sequence.capaciteMax,
              });
              console.log(`[Notification] Annulation envoyée au formateur ${formateur.email}`);
              
              // Si une place se libère (passage de capacité max à disponible)
              if (nbInscritsApres < sequence.capaciteMax && (nbInscritsApres + 1) >= sequence.capaciteMax) {
                const { sendNotificationFormateurPlaceDisponible } = await import('./emailService');
                await sendNotificationFormateurPlaceDisponible({
                  formateurEmail: formateur.email,
                  formateurNom: formateur.nom,
                  formationNom: formation.nom,
                  sequenceNom: sequence.nom,
                  capaciteMax: sequence.capaciteMax,
                  nbInscrits: nbInscritsApres,
                  placesDisponibles: sequence.capaciteMax - nbInscritsApres,
                });
                console.log(`[Notification] Place disponible envoyée au formateur ${formateur.email}`);
              }
            } catch (e) {
              console.error(`[Notification] Erreur envoi notification formateur:`, e);
            }
          }
        }
      }
      
      return { success: true };
    }),

    sendGroupEmail: adminProcedure.input(z.object({ sequenceId: z.number(), type: z.enum(['teaser', 'rappel', 'rappel_j1']) })).mutation(async ({ input }) => {
      const sequence = await db.getSequenceById(input.sequenceId);
      if (!sequence) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'Séquence introuvable' });
      }

      const formation = await db.getFormationById(sequence.formationId);
      if (!formation) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'Formation introuvable' });
      }

      const dates = await db.getDatesBySequence(sequence.id);
      if (dates.length === 0) {
        throw new TRPCError({ code: 'NOT_FOUND', message: 'Aucune date trouvée pour cette séquence' });
      }

      const inscriptions = await db.getInscriptionsBySequence(input.sequenceId);
      const recipients = inscriptions
        .filter(i => i.inscription.statut === 'confirmee' && i.apprenant)
        .map(i => ({
          email: i.apprenant!.email,
          prenom: i.apprenant!.prenom,
          nom: i.apprenant!.nom,
          fonction: i.apprenant!.fonction,
        }));

      console.log(`[Email Group] Envoi de ${recipients.length} emails de type ${input.type}`);
      console.log(`[Email Group] Destinataires:`, recipients.map(r => r.email));

      let formateurNom: string | undefined;
      if (sequence.formateurId) {
        const formateur = await db.getFormateurById(sequence.formateurId);
        formateurNom = formateur?.nom;
      }

      const result = await sendGroupEmail({
        recipients,
        formationNom: formation.nom,
        sequenceNom: sequence.nom,
        type: input.type,
        dates: dates.map(d => ({
          dateDebut: new Date(d.dateDebut),
          dateFin: new Date(d.dateFin),
          ordre: d.ordre,
        })),
        lieu: sequence.lieu,
        formateur: formateurNom,
      });

      console.log(`[Email Group] Résultat: ${result.sent} envoyés, ${result.failed} échoués`);

      return { 
        ...result, 
        message: `${result.sent} email(s) envoyé(s) avec succès, ${result.failed} échoué(s)` 
      };
    }),

    emailPreview: adminProcedure
      .input(z.object({ 
        sequenceId: z.number(), 
        type: z.enum(['teaser', 'rappel', 'rappel_j1']),
        apprenantId: z.number().optional(),
      }))
      .query(async ({ input }) => {
        const { generateEmailPreview } = await import('./emailPreview');
        return await generateEmailPreview(input);
      }),

    exportExcel: adminProcedure.input(z.object({ sequenceId: z.number() })).mutation(async ({ input }) => {
      const sequence = await db.getSequenceById(input.sequenceId);
      if (!sequence) throw new TRPCError({ code: 'NOT_FOUND', message: 'Séquence introuvable' });

      const formation = await db.getFormationById(sequence.formationId);
      if (!formation) throw new TRPCError({ code: 'NOT_FOUND', message: 'Formation introuvable' });

      const inscriptions = await db.getInscriptionsBySequence(input.sequenceId);
      const dates = await db.getDatesBySequence(input.sequenceId);

      const data = inscriptions
        .filter(i => i.apprenant)
        .map(i => ({
          nom: i.apprenant!.nom,
          prenom: i.apprenant!.prenom,
          email: i.apprenant!.email,
          codeEtablissement: i.apprenant!.codeEtablissement,
          fonction: i.apprenant!.fonction,
          statut: i.inscription.statut,
          dateInscription: i.inscription.dateInscription,
        }));

      const buffer = await generateExcelExport({
        formationNom: formation.nom,
        sequenceNom: sequence.nom,
        dates: dates.map(d => ({
          dateDebut: new Date(d.dateDebut),
          dateFin: new Date(d.dateFin),
          ordre: d.ordre,
        })),
        lieu: sequence.lieu,
        publicCible: sequence.publicCible,
        inscriptions: data,
      });

      return {
        buffer: buffer.toString('base64'),
        filename: `inscriptions_${sequence.nom.replace(/\s+/g, '_')}.xlsx`,
      };
    }),

    exportPDF: adminProcedure.input(z.object({ sequenceId: z.number() })).mutation(async ({ input }) => {
      const sequence = await db.getSequenceById(input.sequenceId);
      if (!sequence) throw new TRPCError({ code: 'NOT_FOUND', message: 'Séquence introuvable' });

      const formation = await db.getFormationById(sequence.formationId);
      if (!formation) throw new TRPCError({ code: 'NOT_FOUND', message: 'Formation introuvable' });

      const inscriptions = await db.getInscriptionsBySequence(input.sequenceId);
      const dates = await db.getDatesBySequence(input.sequenceId);

      const data = inscriptions
        .filter(i => i.apprenant)
        .map(i => ({
          nom: i.apprenant!.nom,
          prenom: i.apprenant!.prenom,
          email: i.apprenant!.email,
          codeEtablissement: i.apprenant!.codeEtablissement,
          fonction: i.apprenant!.fonction,
          statut: i.inscription.statut,
          dateInscription: i.inscription.dateInscription,
        }));

      const buffer = await generatePDFExport({
        formationNom: formation.nom,
        sequenceNom: sequence.nom,
        dates: dates.map(d => ({
          dateDebut: new Date(d.dateDebut),
          dateFin: new Date(d.dateFin),
          ordre: d.ordre,
        })),
        lieu: sequence.lieu,
        publicCible: sequence.publicCible,
        inscriptions: data,
      });

      return {
        buffer: buffer.toString('base64'),
        filename: `inscriptions_${sequence.nom.replace(/\s+/g, '_')}.pdf`,
      };
    }),

    exportFeuillePresence: adminProcedure.input(z.object({ sequenceId: z.number() })).mutation(async ({ input }) => {
      const sequence = await db.getSequenceById(input.sequenceId);
      if (!sequence) throw new TRPCError({ code: 'NOT_FOUND', message: 'Séquence introuvable' });

      const formation = await db.getFormationById(sequence.formationId);
      if (!formation) throw new TRPCError({ code: 'NOT_FOUND', message: 'Formation introuvable' });

      const inscriptions = await db.getInscriptionsBySequence(input.sequenceId);
      const dates = await db.getDatesBySequence(input.sequenceId);

      // Récupérer le formateur si défini
      let formateurNom: string | undefined = undefined;
      if (sequence.formateurId) {
        const formateur = await db.getFormateurById(sequence.formateurId);
        if (formateur) {
          formateurNom = formateur.nom;
        }
      }

      const data = inscriptions
        .filter(i => i.inscription.statut === 'confirmee' && i.apprenant)
        .map(i => ({
          nom: i.apprenant!.nom,
          prenom: i.apprenant!.prenom,
          codeEtablissement: i.apprenant!.codeEtablissement,
          fonction: i.apprenant!.fonction,
        }));

      const buffer = await generateFeuillePresence({
        formationNom: formation.nom,
        sequenceNom: sequence.nom,
        dates: dates.map(d => ({
          dateDebut: new Date(d.dateDebut),
          dateFin: new Date(d.dateFin),
          ordre: d.ordre,
        })),
        lieu: sequence.lieu,
        publicCible: sequence.publicCible,
        formateur: formateurNom,
        apprenants: data,
      });

      return {
        buffer: buffer.toString('base64'),
        filename: `feuille_presence_${sequence.nom.replace(/\s+/g, '_')}.pdf`,
      };
    }),
  }),

  // ===== GESTION DES UTILISATEURS =====
  users: router({
    list: adminProcedure.query(async () => {
      return db.getAllUsers();
    }),

    listWithFormateurs: adminProcedure.query(async () => {
      const dbInstance = await import("./db").then(m => m.getDb());
      if (!dbInstance) throw new Error("Base de données non disponible");
      
      const { formateurs, users } = await import("../drizzle/schema");
      const { eq } = await import("drizzle-orm");
      
      // Récupérer tous les formateurs
      const allFormateurs = await dbInstance.select().from(formateurs);
      
      // Récupérer tous les utilisateurs
      const allUsers = await dbInstance.select().from(users);
      
      // Créer une map des formateurs liés à des comptes
      const formateurIdToUser = new Map();
      allUsers.forEach(user => {
        if (user.formateurId) {
          formateurIdToUser.set(user.formateurId, user);
        }
      });
      
      // Combiner les données
      return {
        users: allUsers,
        formateurs: allFormateurs.map(formateur => ({
          ...formateur,
          hasAccount: formateurIdToUser.has(formateur.id),
          user: formateurIdToUser.get(formateur.id) || null,
        })),
      };
    }),

    getById: adminProcedure.input(z.object({ id: z.number() })).query(async ({ input }) => {
      return db.getUserById(input.id);
    }),

    create: adminProcedure.input(z.object({
      openId: z.string().min(1),
      name: z.string().optional(),
      email: z.string().email().optional(),
      username: z.string().min(3).optional(),
      password: z.string().min(6).optional(),
      role: z.enum(["user", "admin", "formateur"]).default("user"),
      isActive: z.boolean().default(true),
      formateurId: z.number().optional(),
    })).mutation(async ({ input }) => {
      await db.createUser(input);
      return { success: true };
    }),

    update: adminProcedure.input(z.object({
      id: z.number(),
      name: z.string().optional(),
      email: z.string().email().optional(),
      username: z.string().min(3).optional(),
      password: z.string().min(6).optional(),
      role: z.enum(["user", "admin", "formateur"]).optional(),
      isActive: z.boolean().optional(),
      formateurId: z.number().optional(),
    })).mutation(async ({ input }) => {
      const { id, ...data } = input;
      await db.updateUser(id, data);
      return { success: true };
    }),

    toggleStatus: adminProcedure.input(z.object({
      id: z.number(),
      isActive: z.boolean(),
    })).mutation(async ({ input }) => {
      await db.toggleUserStatus(input.id, input.isActive);
      return { success: true };
    }),

    delete: adminProcedure.input(z.object({ id: z.number() })).mutation(async ({ input }) => {
      await db.deleteUser(input.id);
      return { success: true };
    }),

    requestPasswordReset: adminProcedure.input(z.object({ id: z.number() })).mutation(async ({ input }) => {
      const user = await db.getUserById(input.id);
      if (!user) {
        throw new Error("Utilisateur introuvable");
      }
      
      if (!user.email) {
        throw new Error("Cet utilisateur n'a pas d'adresse email");
      }

      // Générer un token unique
      const crypto = await import('crypto');
      const token = crypto.randomBytes(32).toString('hex');
      
      // Définir l'expiration à 24h
      const expiresAt = new Date();
      expiresAt.setHours(expiresAt.getHours() + 24);
      
      // Sauvegarder le token en base
      await db.createPasswordResetToken(user.id, token, expiresAt);
      
      // Générer le lien de réinitialisation
      // TODO: Remplacer par l'URL réelle de votre application en production
      const resetLink = `https://votre-domaine.com/reset-password?token=${token}`;
      
      // Envoyer l'email
      const emailService = await import('./emailService');
      const emailSent = await emailService.sendPasswordResetEmail({
        apprenantEmail: user.email,
        apprenantNom: user.name || 'Utilisateur',
        apprenantPrenom: '',
        resetLink,
      });
      
      if (!emailSent) {
        throw new Error("Échec de l'envoi de l'email");
      }
      
      return { success: true, message: "Email de réinitialisation envoyé" };
    }),
  }),

  // ===== GESTION DES TEMPLATES D'EMAILS =====
  emailTemplates: router({
    list: adminProcedure.query(async () => {
      return db.getAllEmailTemplates();
    }),

    getByType: adminProcedure
      .input(z.object({ type: z.string() }))
      .query(async ({ input }) => {
        return db.getEmailTemplateByType(input.type);
      }),

    upsert: adminProcedure
      .input(z.object({
        type: z.string(),
        name: z.string(),
        logoUrl: z.string().nullable(),
        primaryColor: z.string(),
        headerBgColor: z.string(),
        headerTextColor: z.string(),
        headerTitle: z.string(),
        bodyContent: z.string().nullable(),
        footerText: z.string().nullable(),
        active: z.boolean(),
      }))
      .mutation(async ({ input }) => {
        return db.upsertEmailTemplate(input);
      }),

    delete: adminProcedure
      .input(z.object({ type: z.string() }))
      .mutation(async ({ input }) => {
        await db.deleteEmailTemplate(input.type);
        return { success: true };
      }),

    initializeDefaults: adminProcedure.mutation(async () => {
      await db.initializeDefaultEmailTemplates();
      return { success: true };
    }),
  }),

  // ===== GESTION DES RAPPELS =====
  rappels: router({  
    list: adminProcedure.query(async () => {
      return db.getRappels();
    }),

    create: adminProcedure
      .input(z.object({
        nom: z.string(),
        templateType: z.enum(["rappel", "rappelJ1", "rappel3", "rappel4", "rappel5", "rappel6"]),
        timing: z.enum(["pre_formation", "post_formation"]).default("pre_formation"),
        joursAvant: z.number(),
        heureEnvoi: z.string().default("09:00"),
        actif: z.boolean(),
        dateFormationIds: z.array(z.number()).optional(),
        fichier: z.object({
          nomFichier: z.string(),
          urlFichier: z.string(),
          s3Key: z.string(),
          typeFichier: z.string(),
          tailleFichier: z.number(),
        }).nullable().optional(),
      }))
      .mutation(async ({ input }) => {
        const { dateFormationIds, ...rappelData } = input;
        const result: any = await db.createRappel(rappelData);
        
        // Créer les associations rappel-date si des dates sont spécifiées
        if (dateFormationIds && dateFormationIds.length > 0) {
          const rappelId = result.insertId || result[0]?.insertId;
          if (rappelId) {
            await db.createRappelDates(Number(rappelId), dateFormationIds);
          }
        }
        
        return result;
      }),

    update: adminProcedure
      .input(z.object({
        id: z.number(),
        nom: z.string().optional(),
        templateType: z.enum(["rappel", "rappelJ1", "rappel3", "rappel4", "rappel5", "rappel6"]).optional(),
        timing: z.enum(["pre_formation", "post_formation"]).optional(),
        joursAvant: z.number().optional(),
        heureEnvoi: z.string().optional(),
        actif: z.boolean().optional(),
        dateFormationIds: z.array(z.number()).optional(),
        fichier: z.object({
          nomFichier: z.string(),
          urlFichier: z.string(),
          s3Key: z.string(),
          typeFichier: z.string(),
          tailleFichier: z.number(),
        }).nullable().optional(),
      }))
      .mutation(async ({ input }) => {
        const { id, dateFormationIds, ...data } = input;
        await db.updateRappel(id, data);
        
        // Mettre à jour les associations rappel-date si spécifiées
        if (dateFormationIds !== undefined) {
          await db.deleteRappelDates(id);
          if (dateFormationIds.length > 0) {
            await db.createRappelDates(id, dateFormationIds);
          }
        }
        
        return { success: true };
      }),

    delete: adminProcedure
      .input(z.object({ id: z.number() }))
      .mutation(async ({ input }) => {
        await db.deleteRappel(input.id);
        return { success: true };
      }),

    toggleActif: adminProcedure
      .input(z.object({ id: z.number(), actif: z.boolean() }))
      .mutation(async ({ input }) => {
        await db.updateRappel(input.id, { actif: input.actif });
        return { success: true };
      }),

    envoyerAutomatiques: publicProcedure
      .mutation(async () => {
        const { processRappelsAutomatiques } = await import('./rappelScheduler');
        const result = await processRappelsAutomatiques();
        return result;
      }),

    testerRappel: adminProcedure
      .input(z.object({
        sequenceId: z.number(),
        typeRappel: z.enum(['rappel', 'rappelJ1']),
      }))
      .mutation(async ({ input }) => {
        const sequence = await db.getSequenceById(input.sequenceId);
        if (!sequence) {
          throw new TRPCError({ code: 'NOT_FOUND', message: 'Séquence introuvable' });
        }

        const formation = await db.getFormationById(sequence.formationId);
        if (!formation) {
          throw new TRPCError({ code: 'NOT_FOUND', message: 'Formation introuvable' });
        }

        // Récupérer le formateur si présent
        let formateurNom: string | undefined;
        if (sequence.formateurId) {
          const formateur = await db.getFormateurById(sequence.formateurId);
          formateurNom = formateur?.nom;
        }

        // Récupérer les dates de la séquence
        const dates = await db.getDatesBySequence(input.sequenceId);
        
        // Récupérer les inscrits
        const inscrits = await db.getInscriptionsBySequence(input.sequenceId);
        const inscritsConfirmes = inscrits.filter(i => i.inscription.statut === 'confirmee');

        if (inscritsConfirmes.length === 0) {
          throw new TRPCError({ code: 'BAD_REQUEST', message: 'Aucun apprenant inscrit confirmé pour cette séquence' });
        }

        // Envoyer le rappel de test au premier inscrit uniquement
        const premierInscrit = inscritsConfirmes[0];
        const apprenant = premierInscrit.apprenant;
        
        if (!apprenant || !apprenant.email) {
          throw new TRPCError({ code: 'BAD_REQUEST', message: 'Apprenant introuvable ou sans email' });
        }

        const { sendRappelJ7Email, sendRappelJ1Email } = await import('./emailService');

        try {
          if (input.typeRappel === 'rappel') {
            await sendRappelJ7Email({
              apprenantEmail: apprenant.email,
              apprenantPrenom: apprenant.prenom,
              apprenantNom: apprenant.nom,
              apprenantFonction: apprenant.fonction || '',
              formationNom: formation.nom,
              sequenceNom: sequence.nom,
              dates: dates.map((d: any) => ({
                dateDebut: d.dateDebut,
                dateFin: d.dateFin,
                ordre: d.ordre
              })),
              lieu: sequence.lieu || '',
              formateur: formateurNom,
            });
          } else {
            await sendRappelJ1Email({
              apprenantEmail: apprenant.email,
              apprenantPrenom: apprenant.prenom,
              apprenantNom: apprenant.nom,
              apprenantFonction: apprenant.fonction || '',
              formationNom: formation.nom,
              sequenceNom: sequence.nom,
              dates: dates.map((d: any) => ({
                dateDebut: d.dateDebut,
                dateFin: d.dateFin,
                ordre: d.ordre
              })),
              lieu: sequence.lieu || '',
              formateur: formateurNom,
            });
          }

          return {
            success: true,
            message: `Rappel de test envoyé à ${apprenant.email}`,
            destinataire: apprenant.email,
          };
        } catch (error: any) {
          throw new TRPCError({
            code: 'INTERNAL_SERVER_ERROR',
            message: `Erreur lors de l'envoi: ${error.message}`,
          });
        }
      }),

    historique: adminProcedure
      .input(z.object({
        dateDebut: z.string().optional(),
        dateFin: z.string().optional(),
        sequenceId: z.number().optional(),
        statut: z.enum(['success', 'failed']).optional(),
        email: z.string().optional(),
        limit: z.number().default(100),
        offset: z.number().default(0),
      }))
      .query(async ({ input }) => {
        const { getLogsRappelsWithFilters } = await import('./rappelDb');
        return getLogsRappelsWithFilters({
          dateDebut: input.dateDebut ? new Date(input.dateDebut) : undefined,
          dateFin: input.dateFin ? new Date(input.dateFin) : undefined,
          sequenceId: input.sequenceId,
          statut: input.statut,
          email: input.email,
          limit: input.limit,
          offset: input.offset,
        });
      }),

    statistiques: adminProcedure.query(async () => {
      const { getStatsRappels } = await import('./rappelDb');
      return getStatsRappels();
    }),

    evolution: adminProcedure.query(async () => {
      const { getEvolutionEnvois } = await import('./rappelDb');
      return getEvolutionEnvois();
    }),
  }),

  emailConfig: router({
    get: adminProcedure.query(async () => {
      return db.getActiveEmailConfig();
    }),

    upsert: adminProcedure
      .input(z.object({
        provider: z.enum(["resend", "smtp", "simulation"]),
        apiKey: z.string().nullable(),
        fromEmail: z.string().email(),
        fromName: z.string(),
        mode: z.enum(["simulation", "production"]),
        domainVerified: z.boolean(),
        // Champs SMTP
        smtpHost: z.string().nullable().optional(),
        smtpPort: z.number().nullable().optional(),
        smtpSecure: z.enum(["none", "tls", "ssl"]).nullable().optional(),
        smtpUser: z.string().nullable().optional(),
        smtpPassword: z.string().nullable().optional(),
      }))
      .mutation(async ({ input }) => {
        await db.upsertEmailConfig(input);
        return { success: true };
      }),

    testEmail: adminProcedure
      .input(z.object({
        toEmail: z.string().email(),
      }))
      .mutation(async ({ input }) => {
        // Importer le service d'envoi
        const { sendEmail } = await import("./_core/emailSender");
        
        try {
          const success = await sendEmail({
            to: input.toEmail,
            subject: "Test d'envoi d'email - Formation Manager Itinova",
            html: `
              <h1>Test réussi !</h1>
              <p>Cet email de test a été envoyé avec succès depuis votre configuration SMTP.</p>
              <p>Votre configuration d'envoi d'emails fonctionne correctement.</p>
            `,
          });
          
          return { success, message: success ? "Email envoyé avec succès" : "Échec de l'envoi" };
        } catch (error: any) {
          return { success: false, message: error.message };
        }
      }),
  }),

  // ===== PARAMÈTRES DE L'APPLICATION =====
  parametres: router({
    get: adminProcedure.query(async () => {
      const { getParametres } = await import("./parametresDb");
      return getParametres();
    }),

    update: adminProcedure
      .input(z.object({
        urlPublique: z.string().url().optional(),
        delaiExpirationQR: z.number().int().positive().optional(),
        dureeValiditeToken: z.number().int().positive().optional(),
        notificationsActives: z.boolean().optional(),
      }))
      .mutation(async ({ input, ctx }) => {
        const { updateParametres } = await import("./parametresDb");
        await updateParametres(input, ctx.user.id, ctx.user.name || "Administrateur");
        return { success: true };
      }),

    updateUrlPublique: adminProcedure
      .input(z.object({
        urlPublique: z.string().url(),
      }))
      .mutation(async ({ input, ctx }) => {
        const { updateUrlPublique } = await import("./parametresDb");
        await updateUrlPublique(input.urlPublique, ctx.user.id, ctx.user.name || "Administrateur");
        return { success: true };
      }),

    testerUrl: adminProcedure
      .input(z.object({
        url: z.string(),
      }))
      .mutation(async ({ input }) => {
        const { testerUrl } = await import("./parametresDb");
        return testerUrl(input.url);
      }),

    getHistorique: adminProcedure
      .input(z.object({
        limit: z.number().int().positive().default(50),
      }))
      .query(async ({ input }) => {
        const { getHistoriqueParametres } = await import("./parametresDb");
        return getHistoriqueParametres(input.limit);
      }),
  }),

  // ===== ANALYTICS =====
  analytics: router({
    globalStats: adminProcedure.query(async () => {
      return analyticsDb.getGlobalStats();
    }),

    inscriptionsByMonth: adminProcedure
      .input(z.object({
        startDate: z.string().optional(),
        endDate: z.string().optional(),
      }))
      .query(async ({ input }) => {
        const startDate = input.startDate ? new Date(input.startDate) : undefined;
        const endDate = input.endDate ? new Date(input.endDate) : undefined;
        return analyticsDb.getInscriptionsByMonth(startDate, endDate);
      }),

    tauxRemplissageByMonth: adminProcedure.query(async () => {
      return analyticsDb.getTauxRemplissageByMonth();
    }),

    participationByEtablissement: adminProcedure.query(async () => {
      return analyticsDb.getParticipationByEtablissement();
    }),

    participationByFonction: adminProcedure.query(async () => {
      return analyticsDb.getParticipationByFonction();
    }),

    exportExcel: adminProcedure
      .input(z.object({
        startDate: z.string().optional(),
        endDate: z.string().optional(),
      }))
      .mutation(async ({ input }) => {
        const startDate = input.startDate ? new Date(input.startDate) : undefined;
        const endDate = input.endDate ? new Date(input.endDate) : undefined;
        const buffer = await generateAnalyticsExcel(startDate, endDate);
        return {
          data: buffer.toString('base64'),
          filename: `rapport-analytique-${new Date().toISOString().split('T')[0]}.xlsx`,
        };
      }),

    exportPDF: adminProcedure
      .input(z.object({
        startDate: z.string().optional(),
        endDate: z.string().optional(),
      }))
      .mutation(async ({ input }) => {
        const startDate = input.startDate ? new Date(input.startDate) : undefined;
        const endDate = input.endDate ? new Date(input.endDate) : undefined;
        const buffer = await generateAnalyticsPDF(startDate, endDate);
        return {
          data: buffer.toString('base64'),
          filename: `rapport-analytique-${new Date().toISOString().split('T')[0]}.pdf`,
        };
      }),
  }),

  // ===== QUESTIONNAIRES =====
  questionnaires: router({
    list: adminProcedure.query(async () => {
      const questionnaireDb = await import("./questionnaireDb");
      return questionnaireDb.getAllQuestionnaires();
    }),

    getById: adminProcedure
      .input(z.object({ id: z.number() }))
      .query(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        return questionnaireDb.getQuestionnaireById(input.id);
      }),

    create: adminProcedure
      .input(z.object({
        titre: z.string(),
        type: z.enum(["satisfaction", "evaluation_pre", "evaluation_post"]),
        description: z.string().optional(),
        formationId: z.number().nullable().optional(),
        actif: z.boolean().default(true),
        envoiAutomatique: z.boolean().default(false),
        delaiEnvoiJours: z.number().default(1),
      }))
      .mutation(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        const id = await questionnaireDb.createQuestionnaire(input);
        return { id, success: true };
      }),

    update: adminProcedure
      .input(z.object({
        id: z.number(),
        titre: z.string().optional(),
        type: z.enum(["satisfaction", "evaluation_pre", "evaluation_post"]).optional(),
        description: z.string().optional(),
        formationId: z.number().nullable().optional(),
        actif: z.boolean().optional(),
        envoiAutomatique: z.boolean().optional(),
        delaiEnvoiJours: z.number().optional(),
      }))
      .mutation(async ({ input }) => {
        const { id, ...data } = input;
        const questionnaireDb = await import("./questionnaireDb");
        await questionnaireDb.updateQuestionnaire(id, data);
        return { success: true };
      }),

    delete: adminProcedure
      .input(z.object({ id: z.number() }))
      .mutation(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        await questionnaireDb.deleteQuestionnaire(input.id);
        return { success: true };
      }),

    getStats: adminProcedure
      .input(z.object({ id: z.number() }))
      .query(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        return questionnaireDb.getQuestionnaireStats(input.id);
      }),

    getReponsesDetaillees: adminProcedure
      .input(z.object({ id: z.number() }))
      .query(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        return questionnaireDb.getReponsesDetailleesQuestionnaire(input.id);
      }),

    exportExcel: adminProcedure
      .input(z.object({ id: z.number() }))
      .mutation(async ({ input }) => {
        const { exportQuestionnaireToExcel } = await import("./questionnaireExport");
        const buffer = await exportQuestionnaireToExcel(input.id);
        return {
          data: buffer.toString('base64'),
          filename: `questionnaire-${input.id}-${new Date().toISOString().split('T')[0]}.xlsx`,
        };
      }),

    exportPDF: adminProcedure
      .input(z.object({ id: z.number() }))
      .mutation(async ({ input }) => {
        const { exportQuestionnaireToPDF } = await import("./questionnaireExport");
        const buffer = await exportQuestionnaireToPDF(input.id);
        return {
          data: buffer.toString('base64'),
          filename: `questionnaire-${input.id}-${new Date().toISOString().split('T')[0]}.pdf`,
        };
      }),
  }),

  // ===== QUESTIONS =====
  questions: router({
    list: adminProcedure
      .input(z.object({ questionnaireId: z.number() }))
      .query(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        return questionnaireDb.getQuestionsByQuestionnaireId(input.questionnaireId);
      }),

    create: adminProcedure
      .input(z.object({
        questionnaireId: z.number(),
        ordre: z.number(),
        texte: z.string(),
        typeQuestion: z.enum(["choix_multiple", "echelle", "texte_libre", "oui_non"]),
        options: z.string().optional(),
        echelleMin: z.number().optional(),
        echelleMax: z.number().optional(),
        echelleLabelMin: z.string().optional(),
        echelleLabelMax: z.string().optional(),
        obligatoire: z.boolean().default(false),
      }))
      .mutation(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        const id = await questionnaireDb.createQuestion(input);
        return { id, success: true };
      }),

    update: adminProcedure
      .input(z.object({
        id: z.number(),
        ordre: z.number().optional(),
        texte: z.string().optional(),
        typeQuestion: z.enum(["choix_multiple", "echelle", "texte_libre", "oui_non"]).optional(),
        options: z.string().optional(),
        echelleMin: z.number().optional(),
        echelleMax: z.number().optional(),
        echelleLabelMin: z.string().optional(),
        echelleLabelMax: z.string().optional(),
        obligatoire: z.boolean().optional(),
      }))
      .mutation(async ({ input }) => {
        const { id, ...data } = input;
        const questionnaireDb = await import("./questionnaireDb");
        await questionnaireDb.updateQuestion(id, data);
        return { success: true };
      }),

    delete: adminProcedure
      .input(z.object({ id: z.number() }))
      .mutation(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        await questionnaireDb.deleteQuestion(input.id);
        return { success: true };
      }),
  }),

  // ===== RÉPONSES QUESTIONNAIRES (PUBLIC) =====
  reponses: router({
    getByToken: publicProcedure
      .input(z.object({ token: z.string() }))
      .query(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        const envoi = await questionnaireDb.getEnvoiByToken(input.token);
        if (!envoi) return null;

        const questionnaire = await questionnaireDb.getQuestionnaireById(envoi.questionnaireId);
        const questions = await questionnaireDb.getQuestionsByQuestionnaireId(envoi.questionnaireId);
        
        return {
          envoi,
          questionnaire,
          questions,
        };
      }),

    submit: publicProcedure
      .input(z.object({
        token: z.string(),
        reponses: z.array(z.object({
          questionId: z.number(),
          reponseNumerique: z.number().optional(),
          reponseTexte: z.string().optional(),
        })),
      }))
      .mutation(async ({ input }) => {
        const questionnaireDb = await import("./questionnaireDb");
        
        // Récupérer l'envoi
        const envoi = await questionnaireDb.getEnvoiByToken(input.token);
        if (!envoi) throw new Error("Token invalide");
        if (envoi.dateReponse) throw new Error("Questionnaire déjà répondu");

        // Créer la réponse au questionnaire
        const reponseQuestionnaireId = await questionnaireDb.createReponseQuestionnaire({
          questionnaireId: envoi.questionnaireId,
          apprenantId: envoi.apprenantId,
          sequenceId: envoi.sequenceId,
        });

        // Enregistrer chaque réponse
        for (const reponse of input.reponses) {
          await questionnaireDb.createReponseQuestion({
            reponseQuestionnaireId,
            questionId: reponse.questionId,
            reponseNumerique: reponse.reponseNumerique,
            reponseTexte: reponse.reponseTexte,
          });
        }

        // Mettre à jour l'envoi
        await questionnaireDb.updateEnvoiQuestionnaire(envoi.id, {
          dateReponse: new Date(),
        });

        // Envoyer une notification à l'administrateur
        try {
          const { notifyOwner } = await import('./_core/notification');
          const questionnaire = await questionnaireDb.getQuestionnaireById(envoi.questionnaireId);
          
          await notifyOwner({
            title: 'Nouvelle réponse au questionnaire',
            content: `Un apprenant a répondu au questionnaire "${questionnaire?.titre || 'Sans titre'}"`,
          });
        } catch (error) {
          console.error('[Notification] Erreur lors de l\'envoi de la notification:', error);
          // Ne pas bloquer la réponse si la notification échoue
        }

        return { success: true };
      }),

    envoyerAutomatiques: adminProcedure
      .mutation(async () => {
        const { processEnvoisAutomatiques } = await import('./questionnaireScheduler');
        const result = await processEnvoisAutomatiques();
        return result;
      }),
  }),

  // ===== SUIVI QUESTIONNAIRES =====
  questionnaireSuivi: router({
    statsGlobales: adminProcedure.query(async () => {
      const suiviDb = await import("./questionnaireSuiviDb");
      return suiviDb.getStatsGlobales();
    }),

    statsByFormation: adminProcedure.query(async () => {
      const suiviDb = await import("./questionnaireSuiviDb");
      return suiviDb.getStatsByFormation();
    }),

    evolutionTemporelle: adminProcedure
      .input(z.object({
        startDate: z.date().optional(),
        endDate: z.date().optional(),
      }))
      .query(async ({ input }) => {
        const suiviDb = await import("./questionnaireSuiviDb");
        return suiviDb.getEvolutionTemporelle(input.startDate, input.endDate);
      }),

    statsByFormateur: adminProcedure.query(async () => {
      const suiviDb = await import("./questionnaireSuiviDb");
      return suiviDb.getStatsByFormateur();
    }),
  }),

  // ===== ESPACE FORMATEUR =====
  formateur: router({
    calendrier: protectedProcedure
      .input(z.object({
        formateurId: z.number(),
        dateDebut: z.date().optional(),
        dateFin: z.date().optional(),
      }))
      .query(async ({ input }) => {
        const formateurDb = await import("./formateurDb");
        return formateurDb.getCalendrierFormateur(input.formateurId, input.dateDebut, input.dateFin);
      }),

    apprenants: protectedProcedure
      .input(z.object({ sequenceId: z.number() }))
      .query(async ({ input }) => {
        const formateurDb = await import("./formateurDb");
        return formateurDb.getApprenantsSequence(input.sequenceId);
      }),

    supports: protectedProcedure
      .input(z.object({ sequenceId: z.number() }))
      .query(async ({ input }) => {
        const formateurDb = await import("./formateurDb");
        return formateurDb.getSupportsSequence(input.sequenceId);
      }),

    ajouterSupport: protectedProcedure
      .input(z.object({
        sequenceId: z.number(),
        formateurId: z.number(),
        nomFichier: z.string(),
        typeFichier: z.string(),
        tailleFichier: z.number(),
        urlFichier: z.string(),
        s3Key: z.string(),
        description: z.string().optional(),
      }))
      .mutation(async ({ input }) => {
        const formateurDb = await import("./formateurDb");
        return formateurDb.ajouterSupport(input);
      }),

    supprimerSupport: protectedProcedure
      .input(z.object({
        supportId: z.number(),
        formateurId: z.number(),
      }))
      .mutation(async ({ input }) => {
        const formateurDb = await import("./formateurDb");
        const support = await formateurDb.supprimerSupport(input.supportId, input.formateurId);
        
        // Note: La suppression du fichier S3 devrait être gérée par un processus de nettoyage séparé
        // car il n'y a pas de fonction storageDelete dans l'API actuelle
        
        return { success: true, support };
      }),

    validerPresence: protectedProcedure
      .input(z.object({
        inscriptionId: z.number(),
        present: z.boolean(),
      }))
      .mutation(async ({ input }) => {
        const formateurDb = await import("./formateurDb");
        await formateurDb.validerPresence(input.inscriptionId, input.present);
        return { success: true };
      }),

    historique: protectedProcedure
      .input(z.object({
        formateurId: z.number(),
        limit: z.number().optional(),
      }))
      .query(async ({ input }) => {
        const formateurDb = await import("./formateurDb");
        return formateurDb.getHistoriqueFormateur(input.formateurId, input.limit);
      }),

    detailSequence: protectedProcedure
      .input(z.object({
        sequenceId: z.number(),
        formateurId: z.number(),
      }))
      .query(async ({ input }) => {
        const formateurDb = await import("./formateurDb");
        return formateurDb.getDetailSequence(input.sequenceId, input.formateurId);
      }),
  }),

  // ===== ÉTABLISSEMENTS =====
  etablissements: router({
    list: adminProcedure.query(async () => {
      const { getEtablissementsStats } = await import("./etablissementsDb");
      return getEtablissementsStats();
    }),

    getDetail: adminProcedure
      .input(z.object({ codeEtablissement: z.string() }))
      .query(async ({ input }) => {
        const { getEtablissementDetail } = await import("./etablissementsDb");
        return getEtablissementDetail(input.codeEtablissement);
      }),
  }),

  // ===== NOTIFICATIONS =====
  notifications: router({
    envoyerRemerciements: adminProcedure
      .input(z.object({ sequenceId: z.number() }))
      .mutation(async ({ input }) => {
        const { envoyerRemerciementsSequence } = await import("./remerciementScheduler");
        return envoyerRemerciementsSequence(input.sequenceId);
      }),

    envoyerRemerciementsAutomatiques: adminProcedure
      .mutation(async () => {
        const { processRemerciementsAutomatiques } = await import("./remerciementScheduler");
        await processRemerciementsAutomatiques();
        return { success: true };
      }),

    // Historique des notifications
    historique: adminProcedure
      .input(z.object({
        type: z.enum(["remerciement", "notification_formateur_inscription", "notification_formateur_annulation", "alerte_capacite", "notification_liste_attente"]).optional(),
        dateDebut: z.string().optional(),
        dateFin: z.string().optional(),
        statut: z.enum(["success", "failed"]).optional(),
        email: z.string().optional(),
        sequenceId: z.number().optional(),
        limit: z.number().default(50),
        offset: z.number().default(0),
      }))
      .query(async ({ input }) => {
        const { getNotificationLogs } = await import("./notificationLogsDb");
        return getNotificationLogs({
          type: input.type,
          dateDebut: input.dateDebut ? new Date(input.dateDebut) : undefined,
          dateFin: input.dateFin ? new Date(input.dateFin) : undefined,
          statut: input.statut,
          email: input.email,
          sequenceId: input.sequenceId,
          limit: input.limit,
          offset: input.offset,
        });
      }),

    // Statistiques des notifications
    statistiques: adminProcedure
      .input(z.object({
        dateDebut: z.string().optional(),
        dateFin: z.string().optional(),
      }))
      .query(async ({ input }) => {
        const { getNotificationStats } = await import("./notificationLogsDb");
        return getNotificationStats({
          dateDebut: input.dateDebut ? new Date(input.dateDebut) : undefined,
          dateFin: input.dateFin ? new Date(input.dateFin) : undefined,
        });
      }),
  }),

  // Attestations de formation
  attestations: router({
    // Générer une attestation pour une inscription
    generer: adminProcedure
      .input(z.object({
        inscriptionId: z.number(),
      }))
      .mutation(async ({ input }) => {
        const { genererAttestationPDF, enregistrerAttestation, getAttestation } = await import("./attestationService");
        const db = await import("./db").then(m => m.getDb());
        if (!db) throw new Error("Base de données non disponible");
        
        // Vérifier si l'attestation existe déjà
        const { inscriptions } = await import("../drizzle/schema");
        const { eq } = await import("drizzle-orm");
        const [inscription] = await db.select().from(inscriptions).where(eq(inscriptions.id, input.inscriptionId)).limit(1);
        if (!inscription) {
          throw new Error("Inscription introuvable");
        }

        const attestationExistante = await getAttestation(inscription.apprenantId, inscription.sequenceId);
        if (attestationExistante) {
          return {
            success: true,
            attestation: attestationExistante,
            message: "Attestation déjà générée",
          };
        }

        // Générer le PDF
        const { s3Key, pdfUrl } = await genererAttestationPDF(input.inscriptionId);
        
        // Enregistrer dans la base
        const attestationId = await enregistrerAttestation(
          input.inscriptionId,
          inscription.apprenantId,
          inscription.sequenceId,
          s3Key,
          pdfUrl
        );

        const attestation = await getAttestation(inscription.apprenantId, inscription.sequenceId);

        return {
          success: true,
          attestation,
          message: "Attestation générée avec succès",
        };
      }),

    // Récupérer l'attestation d'un apprenant pour une séquence
    get: publicProcedure
      .input(z.object({
        apprenantId: z.number(),
        sequenceId: z.number(),
      }))
      .query(async ({ input }) => {
        const { getAttestation } = await import("./attestationService");
        return getAttestation(input.apprenantId, input.sequenceId);
      }),

    // Récupérer toutes les attestations d'un apprenant
    listApprenant: publicProcedure
      .input(z.object({
        apprenantId: z.number(),
      }))
      .query(async ({ input }) => {
        const { getAttestationsApprenant } = await import("./attestationService");
        return getAttestationsApprenant(input.apprenantId);
      }),

    // Récupérer la configuration des attestations
    getConfig: adminProcedure
      .query(async () => {
        const { getOrCreateConfigAttestation } = await import("./attestationService");
        return getOrCreateConfigAttestation();
      }),

    // Mettre à jour la configuration des attestations
    updateConfig: adminProcedure
      .input(z.object({
        nomSignataire: z.string().optional(),
        fonctionSignataire: z.string().optional(),
        texteAttestation: z.string().optional(),
        logoUrl: z.string().optional(),
        logoS3Key: z.string().optional(),
        signatureUrl: z.string().optional(),
        signatureS3Key: z.string().optional(),
      }))
      .mutation(async ({ input }) => {
        const { updateConfigAttestation } = await import("./attestationService");
        await updateConfigAttestation(input);
        return { success: true };
      }),

    // Récupérer l'historique des envois d'attestations
    historique: adminProcedure
      .query(async () => {
        const { getHistoriqueAttestations } = await import("./attestationsDb");
        return getHistoriqueAttestations();
      }),

    // Récupérer l'historique pour une séquence spécifique
    historiqueBySequence: adminProcedure
      .input(z.object({
        sequenceId: z.number(),
      }))
      .query(async ({ input }) => {
        const { getHistoriqueAttestationsBySequence } = await import("./attestationsDb");
        return getHistoriqueAttestationsBySequence(input.sequenceId);
      }),

    // Générer une prévisualisation du modèle d'attestation
    previsualiser: adminProcedure
      .mutation(async () => {
        const { genererPreviewAttestation } = await import("./attestationService");
        return genererPreviewAttestation();
      }),
  }),

  // ===== PRESENCES (ÉMARGEMENT NUMÉRIQUE) =====
  presences: router({
    // Générer un QR code pour une séquence
    generateQRCode: protectedProcedure
      .input(z.object({
        sequenceId: z.number(),
      }))
      .mutation(async ({ input, ctx }) => {
        const { generateQRToken, generateQRCodeDataURL } = await import("./qrCodeGenerator");
        const { getDb } = await import("./db");
        const { sequences } = await import("../drizzle/schema");
        const { eq } = await import("drizzle-orm");
        
        const db = await getDb();
        if (!db) throw new Error("Database not available");

        // Vérifier que la séquence existe
        const sequence = await db.select().from(sequences).where(eq(sequences.id, input.sequenceId)).limit(1);
        if (sequence.length === 0) {
          throw new Error("Séquence introuvable");
        }

        // Générer un nouveau token si nécessaire
        let token = sequence[0].qrCodeToken;
        if (!token) {
          token = generateQRToken();
          await db.update(sequences).set({ qrCodeToken: token }).where(eq(sequences.id, input.sequenceId));
        }

        // Générer le QR code
        const qrCodeDataURL = await generateQRCodeDataURL(token);

        return {
          token,
          qrCodeDataURL,
        };
      }),

    // Valider une présence (scan QR code ou manuel)
    valider: publicProcedure
      .input(z.object({
        token: z.string().optional(),
        inscriptionId: z.number(),
        dateFormationId: z.number(),
        modeValidation: z.enum(["qrcode", "manuel"]),
        validateurId: z.number().optional(),
        commentaire: z.string().optional(),
      }))
      .mutation(async ({ input }) => {
        const { validerPresence } = await import("./presenceDb");
        const { getDb } = await import("./db");
        const { sequences, inscriptions } = await import("../drizzle/schema");
        const { eq } = await import("drizzle-orm");

        // Si mode QR code, vérifier le token
        if (input.modeValidation === "qrcode" && input.token) {
          const db = await getDb();
          if (!db) throw new Error("Database not available");

          // Récupérer l'inscription pour vérifier la séquence
          const inscription = await db.select().from(inscriptions).where(eq(inscriptions.id, input.inscriptionId)).limit(1);
          if (inscription.length === 0) {
            throw new Error("Inscription introuvable");
          }

          // Vérifier que le token correspond à la séquence
          const sequence = await db.select().from(sequences).where(eq(sequences.id, inscription[0].sequenceId)).limit(1);
          if (sequence.length === 0 || sequence[0].qrCodeToken !== input.token) {
            throw new Error("QR code invalide");
          }
        }

        const result = await validerPresence({
          inscriptionId: input.inscriptionId,
          dateFormationId: input.dateFormationId,
          modeValidation: input.modeValidation,
          validateurId: input.validateurId,
          commentaire: input.commentaire,
        });

        // Vérifier si toutes les présences sont validées
        const { checkAllPresencesValidated } = await import("./presenceDb");
        const allValidated = await checkAllPresencesValidated(input.inscriptionId);

        // Si toutes les présences sont validées, générer l'attestation automatiquement
        if (allValidated) {
          try {
            const { genererAttestationPDF, enregistrerAttestation } = await import("./attestationService");
            
            // Générer le PDF
            const { s3Key, pdfUrl } = await genererAttestationPDF(input.inscriptionId);
            
            // Récupérer les données de l'inscription
            const db = await getDb();
            if (db) {
              const inscriptionData = await db.select().from(inscriptions).where(eq(inscriptions.id, input.inscriptionId)).limit(1);
              if (inscriptionData.length > 0) {
                // Enregistrer l'attestation en base
                await enregistrerAttestation(
                  input.inscriptionId,
                  inscriptionData[0].apprenantId,
                  inscriptionData[0].sequenceId,
                  s3Key,
                  pdfUrl
                );
              }
            }

            return {
              ...result,
              attestationGenerated: true,
              attestationUrl: pdfUrl,
            };
          } catch (error) {
            console.error("Erreur lors de la génération de l'attestation:", error);
            // Ne pas bloquer la validation de présence si l'attestation échoue
          }
        }

        return result;
      }),

    // Lister les présences pour une séquence
    listBySequence: protectedProcedure
      .input(z.object({
        sequenceId: z.number(),
      }))
      .query(async ({ input }) => {
        const { getPresencesBySequence } = await import("./presenceDb");
        return getPresencesBySequence(input.sequenceId);
      }),

    // Lister les présences pour une inscription
    listByInscription: publicProcedure
      .input(z.object({
        inscriptionId: z.number(),
      }))
      .query(async ({ input }) => {
        const { getPresencesByInscription } = await import("./presenceDb");
        return getPresencesByInscription(input.inscriptionId);
      }),

    // Supprimer une présence
    delete: protectedProcedure
      .input(z.object({
        presenceId: z.number(),
      }))
      .mutation(async ({ input }) => {
        const { supprimerPresence } = await import("./presenceDb");
        return supprimerPresence(input.presenceId);
      }),

    // Vérifier si toutes les présences sont validées pour une inscription
    checkAllValidated: publicProcedure
      .input(z.object({
        inscriptionId: z.number(),
      }))
      .query(async ({ input }) => {
        const { checkAllPresencesValidated } = await import("./presenceDb");
        return checkAllPresencesValidated(input.inscriptionId);
      }),
  }),

  // ===== GESTION DES ATTESTATIONS =====
  gestionAttestations: router({
    // Récupérer la configuration d'une formation
    getFormationConfig: adminProcedure
      .input(z.object({ formationId: z.number() }))
      .query(async ({ input }) => {
        const { getFormationConfig } = await import("./gestionAttestationsDb");
        return getFormationConfig(input.formationId);
      }),

    // Mettre à jour la configuration d'une formation
    updateFormationConfig: adminProcedure
      .input(z.object({
        formationId: z.number(),
        modeAttestation: z.enum(["auto", "manuel"]),
        modeEnvoi: z.enum(["auto", "manuel"]),
      }))
      .mutation(async ({ input }) => {
        const { updateFormationConfig } = await import("./gestionAttestationsDb");
        return updateFormationConfig(
          input.formationId,
          input.modeAttestation,
          input.modeEnvoi
        );
      }),

    // Récupérer les apprenants avec leur statut d'attestation
    getApprenantsWithStatus: adminProcedure
      .input(z.object({ formationId: z.number() }))
      .query(async ({ input }) => {
        try {
          const { getApprenantsWithAttestationStatus } = await import("./gestionAttestationsDb");
          const results = await getApprenantsWithAttestationStatus(input.formationId);
          console.log("[gestionAttestations] Found", results.length, "apprenants for formation", input.formationId);
          return results;
        } catch (error) {
          console.error("[gestionAttestations] Error fetching apprenants:", error);
          throw error;
        }
      }),

    // Uploader un document d'attestation
    uploadDocument: adminProcedure
      .input(z.object({
        inscriptionId: z.number(),
        documentUrl: z.string(),
        documentS3Key: z.string(),
      }))
      .mutation(async ({ input, ctx }) => {
        const { uploadAttestationDocument } = await import("./gestionAttestationsDb");
        return uploadAttestationDocument(
          input.inscriptionId,
          input.documentUrl,
          input.documentS3Key,
          ctx.user.id
        );
      }),

    // Supprimer un document d'attestation
    deleteDocument: adminProcedure
      .input(z.object({ attestationId: z.number() }))
      .mutation(async ({ input }) => {
        const { deleteAttestationDocument } = await import("./gestionAttestationsDb");
        return deleteAttestationDocument(input.attestationId);
      }),

    // Envoyer une attestation par email
    sendAttestation: adminProcedure
      .input(z.object({
        attestationId: z.number(),
        apprenantEmail: z.string(),
        apprenantNom: z.string(),
        apprenantPrenom: z.string(),
        formationNom: z.string(),
        pdfUrl: z.string(),
      }))
      .mutation(async ({ input }) => {
        const { sendAttestationEmail } = await import("./emailService");
        const { markAttestationAsSent } = await import("./gestionAttestationsDb");
        
        // Envoyer l'email
        await sendAttestationEmail({
          to: input.apprenantEmail,
          apprenantNom: input.apprenantNom,
          apprenantPrenom: input.apprenantPrenom,
          formationNom: input.formationNom,
          pdfUrl: input.pdfUrl,
        });

        // Marquer comme envoyé
        await markAttestationAsSent(input.attestationId);

        return { success: true };
      }),
  }),
});

export type AppRouter = typeof appRouter;
