import { getDb } from "./db";
import { logsRappels, InsertLogRappel, LogRappel } from "../drizzle/schema";
import { eq, and } from "drizzle-orm";

/**
 * Enregistre l'envoi d'un rappel dans les logs
 */
export async function logRappelEnvoye(data: {
  rappelId: number;
  sequenceId: number;
  apprenantId: number;
  email: string;
  type: string;
  typeEnvoi?: 'automatique' | 'test';
  statut: "succes" | "echec";
  messageErreur?: string;
}): Promise<void> {
  const db = await getDb();
  if (!db) {
    console.warn("[LogsRappels] Base de données non disponible");
    return;
  }

  try {
    await db.insert(logsRappels).values({
      rappelId: data.rappelId,
      sequenceId: data.sequenceId,
      apprenantId: data.apprenantId,
      email: data.email,
      type: data.type as any,
      typeEnvoi: data.typeEnvoi || 'automatique',
      dateEnvoi: new Date(),
      statut: data.statut,
      messageErreur: data.messageErreur || null,
    });
  } catch (error) {
    console.error("[LogsRappels] Erreur lors de l'enregistrement du log:", error);
  }
}

/**
 * Vérifie si un rappel a déjà été envoyé à un apprenant pour une séquence donnée
 */
export async function rappelDejaEnvoye(
  rappelId: number,
  sequenceId: number,
  apprenantId: number
): Promise<boolean> {
  const db = await getDb();
  if (!db) {
    console.warn("[LogsRappels] Base de données non disponible");
    return false;
  }

  try {
    const logs = await db
      .select()
      .from(logsRappels)
      .where(
        and(
          eq(logsRappels.rappelId, rappelId),
          eq(logsRappels.sequenceId, sequenceId),
          eq(logsRappels.apprenantId, apprenantId),
          eq(logsRappels.statut, "succes"),
          eq(logsRappels.typeEnvoi, "automatique") // Ne pas compter les tests
        )
      )
      .limit(1);

    return logs.length > 0;
  } catch (error) {
    console.error("[LogsRappels] Erreur lors de la vérification des doublons:", error);
    return false;
  }
}

/**
 * Récupère tous les logs de rappels pour une séquence donnée
 */
export async function getLogsRappelsBySequence(sequenceId: number): Promise<LogRappel[]> {
  const db = await getDb();
  if (!db) return [];

  try {
    return await db
      .select()
      .from(logsRappels)
      .where(eq(logsRappels.sequenceId, sequenceId))
      .orderBy(logsRappels.dateEnvoi);
  } catch (error) {
    console.error("[LogsRappels] Erreur lors de la récupération des logs:", error);
    return [];
  }
}

/**
 * Récupère tous les logs de rappels échoués récents (dernières 24h)
 */
export async function getLogsRappelsEchoues(): Promise<LogRappel[]> {
  const db = await getDb();
  if (!db) return [];

  try {
    const hier = new Date();
    hier.setDate(hier.getDate() - 1);

    return await db
      .select()
      .from(logsRappels)
      .where(
        and(
          eq(logsRappels.statut, "echec"),
          // Note: Drizzle ne supporte pas directement les comparaisons de dates
          // On récupère tous les échecs et on filtre en JS
        )
      )
      .orderBy(logsRappels.dateEnvoi);
  } catch (error) {
    console.error("[LogsRappels] Erreur lors de la récupération des échecs:", error);
    return [];
  }
}

/**
 * Compte le nombre de rappels envoyés avec succès pour une séquence
 */
export async function countRappelsEnvoyesSequence(sequenceId: number): Promise<number> {
  const db = await getDb();
  if (!db) return 0;

  try {
    const logs = await db
      .select()
      .from(logsRappels)
      .where(
        and(
          eq(logsRappels.sequenceId, sequenceId),
          eq(logsRappels.statut, "succes")
        )
      );

    return logs.length;
  } catch (error) {
    console.error("[LogsRappels] Erreur lors du comptage:", error);
    return 0;
  }
}

/**
 * Récupère les logs de rappels avec filtres
 */
export async function getLogsRappelsWithFilters(filters: {
  dateDebut?: Date;
  dateFin?: Date;
  sequenceId?: number;
  statut?: "succes" | "echec";
  email?: string;
  limit?: number;
  offset?: number;
}): Promise<LogRappel[]> {
  const db = await getDb();
  if (!db) return [];

  try {
    let query = db.select().from(logsRappels);

    const conditions = [];
    if (filters.sequenceId) {
      conditions.push(eq(logsRappels.sequenceId, filters.sequenceId));
    }
    if (filters.statut) {
      conditions.push(eq(logsRappels.statut, filters.statut));
    }
    if (filters.email) {
      // Note: Drizzle ne supporte pas LIKE directement, on filtre en JS
    }

    if (conditions.length > 0) {
      query = query.where(and(...conditions)) as any;
    }

    let logs = await query.orderBy(logsRappels.dateEnvoi).limit(filters.limit || 100).offset(filters.offset || 0);

    // Filtrer par email si nécessaire
    if (filters.email) {
      logs = logs.filter(log => log.email.toLowerCase().includes(filters.email!.toLowerCase()));
    }

    // Filtrer par date si nécessaire
    if (filters.dateDebut) {
      logs = logs.filter(log => new Date(log.dateEnvoi) >= filters.dateDebut!);
    }
    if (filters.dateFin) {
      logs = logs.filter(log => new Date(log.dateEnvoi) <= filters.dateFin!);
    }

    return logs;
  } catch (error) {
    console.error("[LogsRappels] Erreur lors de la récupération avec filtres:", error);
    return [];
  }
}

/**
 * Calcule les statistiques des rappels
 */
export async function getStatsRappels(): Promise<{
  totalEnvoyes: number;
  totalSucces: number;
  totalEchecs: number;
  tauxSucces: number;
  emailsProblematiques: Array<{ email: string; nbEchecs: number }>;
}> {
  const db = await getDb();
  if (!db) {
    return {
      totalEnvoyes: 0,
      totalSucces: 0,
      totalEchecs: 0,
      tauxSucces: 0,
      emailsProblematiques: [],
    };
  }

  try {
    const logs = await db.select().from(logsRappels);

    const totalEnvoyes = logs.length;
    const totalSucces = logs.filter(l => l.statut === "succes").length;
    const totalEchecs = logs.filter(l => l.statut === "echec").length;
    const tauxSucces = totalEnvoyes > 0 ? (totalSucces / totalEnvoyes) * 100 : 0;

    // Compter les échecs par email
    const echecsParEmail = new Map<string, number>();
    logs.filter(l => l.statut === "echec").forEach(log => {
      const count = echecsParEmail.get(log.email) || 0;
      echecsParEmail.set(log.email, count + 1);
    });

    const emailsProblematiques = Array.from(echecsParEmail.entries())
      .map(([email, nbEchecs]) => ({ email, nbEchecs }))
      .sort((a, b) => b.nbEchecs - a.nbEchecs)
      .slice(0, 10); // Top 10

    return {
      totalEnvoyes,
      totalSucces,
      totalEchecs,
      tauxSucces,
      emailsProblematiques,
    };
  } catch (error) {
    console.error("[LogsRappels] Erreur lors du calcul des statistiques:", error);
    return {
      totalEnvoyes: 0,
      totalSucces: 0,
      totalEchecs: 0,
      tauxSucces: 0,
      emailsProblematiques: [],
    };
  }
}

/**
 * Récupère l'évolution des envois par jour
 */
export async function getEvolutionEnvois(): Promise<Array<{
  date: string;
  nbEnvoyes: number;
  nbSucces: number;
  nbEchecs: number;
}>> {
  const db = await getDb();
  if (!db) return [];

  try {
    const logs = await db.select().from(logsRappels).orderBy(logsRappels.dateEnvoi);

    // Grouper par jour
    const parJour = new Map<string, { nbEnvoyes: number; nbSucces: number; nbEchecs: number }>();
    
    logs.forEach(log => {
      const dateStr = new Date(log.dateEnvoi).toISOString().split('T')[0];
      const stats = parJour.get(dateStr) || { nbEnvoyes: 0, nbSucces: 0, nbEchecs: 0 };
      stats.nbEnvoyes++;
      if (log.statut === "succes") stats.nbSucces++;
      else stats.nbEchecs++;
      parJour.set(dateStr, stats);
    });

    return Array.from(parJour.entries())
      .map(([date, stats]) => ({ date, ...stats }))
      .sort((a, b) => a.date.localeCompare(b.date));
  } catch (error) {
    console.error("[LogsRappels] Erreur lors de la récupération de l'évolution:", error);
    return [];
  }
}

/**
 * Récupère les logs échoués à réessayer
 */
export async function getLogsAReessayer(): Promise<LogRappel[]> {
  const db = await getDb();
  if (!db) return [];

  try {
    const maintenant = new Date();
    const logs = await db
      .select()
      .from(logsRappels)
      .where(
        and(
          eq(logsRappels.statut, "echec"),
          // Limiter à 3 tentatives maximum
        )
      );

    // Filtrer en JS pour les conditions complexes
    return logs.filter(log => {
      if (log.nbTentatives >= 3) return false;
      if (!log.prochainEssai) return true; // Premier essai
      return new Date(log.prochainEssai) <= maintenant;
    });
  } catch (error) {
    console.error("[LogsRappels] Erreur lors de la récupération des logs à réessayer:", error);
    return [];
  }
}
