import { getDb } from "./db";
import { rappels, sequences, inscriptions, apprenants, formations, datesFormation, formateurs } from "../drizzle/schema";
import { eq, and, gte, lte, sql, inArray } from "drizzle-orm";
import { sendRappelJ7Email, sendRappelJ1Email } from "./emailService";
import { logRappelEnvoye, rappelDejaEnvoye } from "./rappelDb";
import { notifyOwner } from "./_core/notification";
import { processRemerciementsAutomatiques } from "./remerciementScheduler";

/**
 * Job automatique qui vérifie quotidiennement les séquences à venir
 * et envoie les rappels J-7 et J-1 aux apprenants inscrits
 */
export async function processRappelsAutomatiques() {
  console.log("[Rappels] Démarrage du traitement des rappels automatiques...");
  
  const db = await getDb();
  if (!db) {
    console.error("[Rappels] Base de données non disponible");
    return;
  }

  try {
    // Récupérer tous les rappels actifs
    const rappelsActifs = await db
      .select()
      .from(rappels)
      .where(eq(rappels.actif, true));

    console.log(`[Rappels] ${rappelsActifs.length} rappel(s) actif(s) trouvé(s)`);

    for (const rappel of rappelsActifs) {
      await processRappel(rappel);
    }

    console.log("[Rappels] Traitement terminé");
  } catch (error) {
    console.error("[Rappels] Erreur lors du traitement des rappels:", error);
  }
}

async function processRappel(rappel: any) {
  console.log(`[Rappels] Traitement du rappel: ${rappel.nom} (${rappel.timing === 'pre_formation' ? 'J-' : 'J+'}${rappel.joursAvant})`);

  const db = await getDb();
  if (!db) return;

  try {
    // Calculer la date cible selon le timing (pré ou post-formation)
    const dateCible = new Date();
    if (rappel.timing === 'pre_formation') {
      dateCible.setDate(dateCible.getDate() + rappel.joursAvant);
    } else {
      dateCible.setDate(dateCible.getDate() - rappel.joursAvant);
    }
    dateCible.setHours(0, 0, 0, 0);

    const dateCibleFin = new Date(dateCible);
    dateCibleFin.setHours(23, 59, 59, 999);

    console.log(`[Rappels] Recherche des dates de formation le ${dateCible.toLocaleDateString()}`);

    // Vérifier si ce rappel est associé à des dates spécifiques
    const datesAssociees = rappel.dateFormationIds || [];
    const filtreDates = datesAssociees.length > 0;
    
    console.log(`[Rappels] ${filtreDates ? `Filtré sur ${datesAssociees.length} date(s) spécifique(s)` : 'Toutes les dates'}`);

    // Récupérer les dates de formation concernées
    let datesConcernees;
    if (filtreDates) {
      // Seulement les dates associées à ce rappel
      datesConcernees = await db
        .select({
          dateFormation: datesFormation,
          sequence: sequences,
          formation: formations,
          formateur: formateurs,
        })
        .from(datesFormation)
        .leftJoin(sequences, eq(datesFormation.sequenceId, sequences.id))
        .leftJoin(formations, eq(sequences.formationId, formations.id))
        .leftJoin(formateurs, eq(sequences.formateurId, formateurs.id))
        .where(
          and(
            inArray(datesFormation.id, datesAssociees),
            gte(datesFormation.dateDebut, dateCible),
            lte(datesFormation.dateDebut, dateCibleFin)
          )
        );
    } else {
      // Toutes les dates correspondant à la date cible
      datesConcernees = await db
        .select({
          dateFormation: datesFormation,
          sequence: sequences,
          formation: formations,
          formateur: formateurs,
        })
        .from(datesFormation)
        .leftJoin(sequences, eq(datesFormation.sequenceId, sequences.id))
        .leftJoin(formations, eq(sequences.formationId, formations.id))
        .leftJoin(formateurs, eq(sequences.formateurId, formateurs.id))
        .where(
          and(
            gte(datesFormation.dateDebut, dateCible),
            lte(datesFormation.dateDebut, dateCibleFin)
          )
        );
    }

    console.log(`[Rappels] ${datesConcernees.length} date(s) de formation trouvée(s)`);

    // Grouper les dates par séquence pour envoyer un seul email par apprenant
    const sequencesMap = new Map<number, { sequence: any; formation: any; formateur: any; dates: any[] }>();
    
    for (const { dateFormation, sequence, formation, formateur } of datesConcernees) {
      if (!dateFormation || !sequence || !formation) continue;
      
      if (!sequencesMap.has(sequence.id)) {
        sequencesMap.set(sequence.id, {
          sequence,
          formation,
          formateur,
          dates: [],
        });
      }
      sequencesMap.get(sequence.id)!.dates.push(dateFormation);
    }

    console.log(`[Rappels] ${sequencesMap.size} séquence(s) concernée(s)`);

    for (const { sequence, formation, formateur, dates: datesRappel } of Array.from(sequencesMap.values())) {
      if (!sequence || !formation) continue;

      // Récupérer toutes les dates de cette séquence pour l'email
      const toutesLesDates = await db
        .select()
        .from(datesFormation)
        .where(eq(datesFormation.sequenceId, sequence.id))
        .orderBy(datesFormation.ordre);

      const formateurNom = formateur?.nom || "";

      // Récupérer tous les apprenants inscrits à cette séquence
      const inscrits = await db
        .select({
          inscription: inscriptions,
          apprenant: apprenants,
        })
        .from(inscriptions)
        .leftJoin(apprenants, eq(inscriptions.apprenantId, apprenants.id))
        .where(
          and(
            eq(inscriptions.sequenceId, sequence.id),
            eq(inscriptions.statut, "confirmee")
          )
        );

      console.log(`[Rappels] ${inscrits.length} apprenant(s) inscrit(s) à la séquence ${sequence.nom}`);

      // Envoyer le rappel à chaque apprenant
      let nbEnvoyes = 0;
      let nbEchecs = 0;
      const echecs: Array<{ email: string; erreur: string }> = [];

      for (const { apprenant } of inscrits) {
        if (!apprenant || !apprenant.email) continue;

        // Vérifier si le rappel a déjà été envoyé
        const dejaEnvoye = await rappelDejaEnvoye(rappel.id, sequence.id, apprenant.id);
        if (dejaEnvoye) {
          console.log(`[Rappels] Rappel déjà envoyé à ${apprenant.email} pour la séquence ${sequence.nom} - ignoré`);
          continue;
        }

        try {
          // Choisir la fonction d'envoi selon le type de rappel
          if (rappel.templateType === "rappel") {
            // Rappel J-7
            await sendRappelJ7Email({
              apprenantEmail: apprenant.email,
              apprenantPrenom: apprenant.prenom,
              apprenantNom: apprenant.nom,
              apprenantFonction: apprenant.fonction,
              formationNom: formation.nom,
              sequenceNom: sequence.nom,
              dates: toutesLesDates.map(d => ({
                dateDebut: d.dateDebut,
                dateFin: d.dateFin,
                ordre: d.ordre
              })),
              lieu: sequence.lieu || "",
              formateur: formateurNom,
              attachmentUrl: rappel.urlFichier || undefined,
              attachmentFilename: rappel.nomFichier || undefined,
              attachmentMimeType: rappel.typeFichier || undefined,
            });
            console.log(`[Rappels] Email J-7 envoyé à ${apprenant.email} pour la séquence ${sequence.nom}`);
          } else if (rappel.templateType === "rappelJ1") {
            // Rappel J-1
            await sendRappelJ1Email({
              apprenantEmail: apprenant.email,
              apprenantPrenom: apprenant.prenom,
              apprenantNom: apprenant.nom,
              apprenantFonction: apprenant.fonction,
              formationNom: formation.nom,
              sequenceNom: sequence.nom,
              dates: toutesLesDates.map(d => ({
                dateDebut: d.dateDebut,
                dateFin: d.dateFin,
                ordre: d.ordre
              })),
              lieu: sequence.lieu || "",
              formateur: formateurNom,
              attachmentUrl: rappel.urlFichier || undefined,
              attachmentFilename: rappel.nomFichier || undefined,
              attachmentMimeType: rappel.typeFichier || undefined,
            });
            console.log(`[Rappels] Email J-1 envoyé à ${apprenant.email} pour la séquence ${sequence.nom}`);
          }

          // Logger le succès
          await logRappelEnvoye({
            rappelId: rappel.id,
            sequenceId: sequence.id,
            apprenantId: apprenant.id,
            emailDestinataire: apprenant.email,
            typeRappel: rappel.templateType,
            statut: "success",
            dateSequence: toutesLesDates[0].dateDebut,
          });
          nbEnvoyes++;
        } catch (emailError: any) {
          const messageErreur = emailError?.message || String(emailError);
          console.error(`[Rappels] Erreur lors de l'envoi à ${apprenant.email}:`, emailError);
          
          // Logger l'échec
          await logRappelEnvoye({
            rappelId: rappel.id,
            sequenceId: sequence.id,
            apprenantId: apprenant.id,
            emailDestinataire: apprenant.email,
            typeRappel: rappel.templateType,
            statut: "failed",
            messageErreur: messageErreur,
            dateSequence: toutesLesDates[0].dateDebut,
          });
          nbEchecs++;
          echecs.push({ email: apprenant.email, erreur: messageErreur });
        }
      }

      // Notifier l'administrateur en cas d'échecs
      if (nbEchecs > 0) {
        const listeEchecs = echecs.map(e => `- ${e.email}: ${e.erreur}`).join("\n");
        await notifyOwner({
          title: `⚠️ Échecs d'envoi de rappels`,
          content: `${nbEchecs} rappel(s) n'ont pas pu être envoyés pour la séquence "${sequence.nom}" (${formation.nom}):\n\n${listeEchecs}\n\nRappel: ${rappel.nom} (J-${rappel.joursAvant})\nEnvois réussis: ${nbEnvoyes}`,
        });
      }

      console.log(`[Rappels] Séquence ${sequence.nom}: ${nbEnvoyes} envoi(s) réussi(s), ${nbEchecs} échec(s)`);
    }

    // Mettre à jour la date de dernière exécution
    await db
      .update(rappels)
      .set({ derniereExecution: new Date() })
      .where(eq(rappels.id, rappel.id));

  } catch (error) {
    console.error(`[Rappels] Erreur lors du traitement du rappel ${rappel.nom}:`, error);
  }
}

/**
 * Initialise le scheduler pour exécuter les rappels automatiques
 * Vérifie toutes les heures s'il y a des rappels à envoyer
 */
export function initRappelScheduler() {
  console.log("[Rappels] Initialisation du scheduler de rappels automatiques");

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

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

  console.log("[Rappels] Scheduler initialisé - vérification toutes les heures (rappels + remerciements)");
}
