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 } from "./emailService";
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: adminProcedure.query(async () => {
      return db.getFormations();
    }),

    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: adminProcedure.query(async () => {
      return db.getApprenants();
    }),

    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 };
    }),

    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();
    }),

    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),
    })).mutation(async ({ input }) => {
      await db.createFormateur(input);
      return { success: true };
    }),

    update: adminProcedure.input(z.object({
      id: z.number(),
      nom: z.string().min(1).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: adminProcedure.query(async () => {
      const seqs = await db.getSequencesWithFormation();
      // 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;
    }),

    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);
      return { ...seq, dates };
    }),

    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({
    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);
    }),

    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,
        });
      }

      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' });
      }

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

      return { success: true };
    }),

    updateStatut: adminProcedure.input(z.object({
      id: z.number(),
      statut: z.enum(['confirmee', 'liste_attente', 'annulee']),
    })).mutation(async ({ input }) => {
      await db.updateInscription(input.id, { statut: input.statut });
      return { success: true };
    }),

    sendGroupEmail: adminProcedure.input(z.object({
      sequenceId: z.number(),
      type: z.enum(['teaser', 'rappel']),
    })).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,
        }));

      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,
      });

      return result;
    }),

    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();
    }),

    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"]).default("user"),
      isActive: z.boolean().default(true),
    })).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"]).optional(),
      isActive: z.boolean().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.getAllRappels();
    }),

    create: adminProcedure
      .input(z.object({
        nom: z.string(),
        joursAvant: z.number(),
        sujet: z.string(),
        contenu: z.string(),
        actif: z.boolean(),
      }))
      .mutation(async ({ input }) => {
        return db.createRappel(input);
      }),

    update: adminProcedure
      .input(z.object({
        id: z.number(),
        nom: z.string(),
        joursAvant: z.number(),
        sujet: z.string(),
        contenu: z.string(),
        actif: z.boolean(),
      }))
      .mutation(async ({ input }) => {
        return db.updateRappel(input);
      }),

    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({ id: input.id, actif: input.actif });
        return { success: true };
      }),
  }),

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

    upsert: adminProcedure
      .input(z.object({
        provider: z.string(),
        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 };
        }
      }),
  }),

  // ===== 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`,
        };
      }),
  }),

  // ===== É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);
      }),
  }),
});

export type AppRouter = typeof appRouter;
