import { getDb } from "./db";
import { logsRappels, sequences, inscriptions, apprenants, formations, datesFormation, rappels, formateurs } from "../drizzle/schema";
import { eq, and } from "drizzle-orm";
import { sendRappelJ7Email, sendRappelJ1Email } from "./emailService";
import { getLogsAReessayer } from "./rappelDb";
import { notifyOwner } from "./_core/notification";

/**
 * Calcule le délai avant le prochain essai selon le nombre de tentatives
 * Délai exponentiel : 1h, 4h, 24h
 */
function calculerProchainEssai(nbTentatives: number): Date {
  const maintenant = new Date();
  let delaiHeures = 1; // 1 heure par défaut

  if (nbTentatives === 1) {
    delaiHeures = 1; // 1ère tentative échouée → réessayer dans 1h
  } else if (nbTentatives === 2) {
    delaiHeures = 4; // 2ème tentative échouée → réessayer dans 4h
  } else {
    delaiHeures = 24; // 3ème tentative échouée → réessayer dans 24h (mais on arrête à 3)
  }

  maintenant.setHours(maintenant.getHours() + delaiHeures);
  return maintenant;
}

/**
 * Réessaye d'envoyer les rappels échoués
 */
export async function processRappelRetry() {
  console.log("[Rappels Retry] Démarrage du processus de réessai");

  const db = await getDb();
  if (!db) {
    console.error("[Rappels Retry] Base de données non disponible");
    return;
  }

  try {
    // Récupérer les logs à réessayer
    const logsAReessayer = await getLogsAReessayer();
    
    if (logsAReessayer.length === 0) {
      console.log("[Rappels Retry] Aucun rappel à réessayer");
      return;
    }

    console.log(`[Rappels Retry] ${logsAReessayer.length} rappel(s) à réessayer`);

    let nbSucces = 0;
    let nbEchecs = 0;

    for (const log of logsAReessayer) {
      try {
        // Récupérer les informations de la séquence
        const sequence = await db
          .select()
          .from(sequences)
          .where(eq(sequences.id, log.sequenceId))
          .limit(1);

        if (sequence.length === 0) {
          console.warn(`[Rappels Retry] Séquence ${log.sequenceId} introuvable`);
          continue;
        }

        const seq = sequence[0];

        // Récupérer la formation
        const formation = await db
          .select()
          .from(formations)
          .where(eq(formations.id, seq.formationId))
          .limit(1);

        if (formation.length === 0) {
          console.warn(`[Rappels Retry] Formation ${seq.formationId} introuvable`);
          continue;
        }

        // Récupérer l'apprenant
        const apprenant = await db
          .select()
          .from(apprenants)
          .where(eq(apprenants.id, log.apprenantId))
          .limit(1);

        if (apprenant.length === 0 || !apprenant[0].email) {
          console.warn(`[Rappels Retry] Apprenant ${log.apprenantId} introuvable ou sans email`);
          continue;
        }

        // Récupérer les dates de la séquence
        const dates = await db
          .select()
          .from(datesFormation)
          .where(eq(datesFormation.sequenceId, seq.id))
          .orderBy(datesFormation.ordre);

        // Récupérer le formateur si disponible
        let formateurNom = "";
        if (seq.formateurId) {
          const formateur = await db
            .select()
            .from(formateurs)
            .where(eq(formateurs.id, seq.formateurId))
            .limit(1);
          if (formateur.length > 0) {
            formateurNom = formateur[0].nom;
          }
        }

        // Réessayer l'envoi
        try {
          if (log.typeRappel === "rappel") {
            await sendRappelJ7Email({
              apprenantEmail: apprenant[0].email,
              apprenantPrenom: apprenant[0].prenom,
              apprenantNom: apprenant[0].nom,
              apprenantFonction: apprenant[0].fonction,
              formationNom: formation[0].nom,
              sequenceNom: seq.nom,
              dates: dates.map(d => ({
                dateDebut: d.dateDebut,
                dateFin: d.dateFin,
                ordre: d.ordre
              })),
              lieu: seq.lieu || "",
            });
          } else if (log.typeRappel === "rappelJ1") {
            await sendRappelJ1Email({
              apprenantEmail: apprenant[0].email,
              apprenantPrenom: apprenant[0].prenom,
              apprenantNom: apprenant[0].nom,
              apprenantFonction: apprenant[0].fonction,
              formationNom: formation[0].nom,
              sequenceNom: seq.nom,
              dates: dates.map(d => ({
                dateDebut: d.dateDebut,
                dateFin: d.dateFin,
                ordre: d.ordre
              })),
              lieu: seq.lieu || "",
            });
          }

          // Succès : mettre à jour le log
          await db
            .update(logsRappels)
            .set({
              statut: "success",
              nbTentatives: log.nbTentatives + 1,
              prochainEssai: null,
            })
            .where(eq(logsRappels.id, log.id));

          console.log(`[Rappels Retry] Succès pour ${apprenant[0].email} après ${log.nbTentatives + 1} tentative(s)`);
          nbSucces++;
        } catch (emailError: any) {
          const messageErreur = emailError?.message || String(emailError);
          const nouvelleTentative = log.nbTentatives + 1;

          if (nouvelleTentative >= 3) {
            // Abandon après 3 tentatives
            await db
              .update(logsRappels)
              .set({
                statut: "failed",
                nbTentatives: nouvelleTentative,
                messageErreur: `Abandon après 3 tentatives: ${messageErreur}`,
                prochainEssai: null,
              })
              .where(eq(logsRappels.id, log.id));

            console.error(`[Rappels Retry] Abandon pour ${apprenant[0].email} après 3 tentatives`);
            
            // Notifier l'admin
            await notifyOwner({
              title: `❌ Abandon d'envoi de rappel`,
              content: `Le rappel pour ${apprenant[0].email} (${seq.nom}) a échoué 3 fois et a été abandonné.\\n\\nDernière erreur: ${messageErreur}`,
            });
          } else {
            // Planifier un nouvel essai
            const prochainEssai = calculerProchainEssai(nouvelleTentative);
            await db
              .update(logsRappels)
              .set({
                nbTentatives: nouvelleTentative,
                messageErreur: messageErreur,
                prochainEssai: prochainEssai,
              })
              .where(eq(logsRappels.id, log.id));

            console.log(`[Rappels Retry] Échec pour ${apprenant[0].email}, tentative ${nouvelleTentative}/3. Prochain essai: ${prochainEssai.toLocaleString()}`);
          }
          nbEchecs++;
        }
      } catch (error) {
        console.error(`[Rappels Retry] Erreur lors du traitement du log ${log.id}:`, error);
      }
    }

    console.log(`[Rappels Retry] Traitement terminé: ${nbSucces} succès, ${nbEchecs} échecs`);
  } catch (error) {
    console.error("[Rappels Retry] Erreur lors du processus de retry:", error);
  }
}

/**
 * Initialise le scheduler de retry
 * Vérifie toutes les heures s'il y a des rappels à réessayer
 */
export function initRappelRetryScheduler() {
  console.log("[Rappels Retry] Initialisation du scheduler de réessai automatique");

  // Exécuter immédiatement au démarrage
  processRappelRetry();

  // Puis exécuter toutes les heures
  setInterval(() => {
    processRappelRetry();
  }, 60 * 60 * 1000); // 1 heure en millisecondes

  console.log("[Rappels Retry] Scheduler de réessai initialisé - vérification toutes les heures");
}
