import { eq, sql, and, gte, lte, desc } from "drizzle-orm";
import { getDb } from "./db";
import { 
  sequences,
  formations,
  formateurs,
  datesFormation,
  inscriptions,
  apprenants,
  supportsFormation
} from "../drizzle/schema";

/**
 * Récupère le calendrier des interventions d'un formateur
 * @param formateurId - ID du formateur
 * @param dateDebut - Date de début optionnelle
 * @param dateFin - Date de fin optionnelle
 */
export async function getCalendrierFormateur(formateurId: number, dateDebut?: Date, dateFin?: Date) {
  const db = await getDb();
  if (!db) return [];

  const conditions = [eq(sequences.formateurId, formateurId)];
  
  if (dateDebut) {
    conditions.push(gte(datesFormation.dateDebut, dateDebut));
  }
  if (dateFin) {
    conditions.push(lte(datesFormation.dateFin, dateFin));
  }

  const results = await db
    .select({
      sequenceId: sequences.id,
      sequenceNom: sequences.nom,
      formationId: formations.id,
      formationNom: formations.nom,
      dateId: datesFormation.id,
      dateDebut: datesFormation.dateDebut,
      dateFin: datesFormation.dateFin,
      ordre: datesFormation.ordre,
      lieu: sequences.lieu,
      nbInscrits: sql<number>`(
        SELECT COUNT(*) 
        FROM inscriptions 
        WHERE inscriptions.sequenceId = ${sequences.id}
        AND inscriptions.statut IN ('confirmee', 'liste_attente')
      )`,
    })
    .from(sequences)
    .innerJoin(formations, eq(sequences.formationId, formations.id))
    .innerJoin(datesFormation, eq(datesFormation.sequenceId, sequences.id))
    .where(conditions.length > 0 ? and(...conditions) : undefined)
    .orderBy(datesFormation.dateDebut);

  return results;
}

/**
 * Récupère la liste des apprenants inscrits à une séquence
 * @param sequenceId - ID de la séquence
 */
export async function getApprenantsSequence(sequenceId: number) {
  const db = await getDb();
  if (!db) return [];

  const results = await db
    .select({
      inscriptionId: inscriptions.id,
      apprenantId: apprenants.id,
      nom: apprenants.nom,
      prenom: apprenants.prenom,
      email: apprenants.email,
      fonction: apprenants.fonction,
      codeEtablissement: apprenants.codeEtablissement,
      statut: inscriptions.statut,
      dateInscription: inscriptions.dateInscription,
      // presenceValidee n'existe pas dans le schéma actuel
    })
    .from(inscriptions)
    .innerJoin(apprenants, eq(inscriptions.apprenantId, apprenants.id))
    .where(eq(inscriptions.sequenceId, sequenceId))
    .orderBy(apprenants.nom, apprenants.prenom);

  return results;
}

/**
 * Récupère les supports de formation d'une séquence
 * @param sequenceId - ID de la séquence
 */
export async function getSupportsSequence(sequenceId: number) {
  const db = await getDb();
  if (!db) return [];

  const results = await db
    .select({
      id: supportsFormation.id,
      nomFichier: supportsFormation.nomFichier,
      typeFichier: supportsFormation.typeFichier,
      tailleFichier: supportsFormation.tailleFichier,
      urlFichier: supportsFormation.urlFichier,
      description: supportsFormation.description,
      formateurNom: formateurs.nom,
      createdAt: supportsFormation.createdAt,
    })
    .from(supportsFormation)
    .innerJoin(formateurs, eq(supportsFormation.formateurId, formateurs.id))
    .where(eq(supportsFormation.sequenceId, sequenceId))
    .orderBy(desc(supportsFormation.createdAt));

  return results;
}

/**
 * Ajoute un support de formation
 * @param support - Données du support à ajouter
 */
export async function ajouterSupport(support: {
  sequenceId: number;
  formateurId: number;
  nomFichier: string;
  typeFichier: string;
  tailleFichier: number;
  urlFichier: string;
  s3Key: string;
  description?: string;
}) {
  const db = await getDb();
  if (!db) throw new Error("Database not available");

  const [result] = await db.insert(supportsFormation).values(support);
  return result;
}

/**
 * Supprime un support de formation
 * @param supportId - ID du support à supprimer
 * @param formateurId - ID du formateur (pour vérification)
 */
export async function supprimerSupport(supportId: number, formateurId: number) {
  const db = await getDb();
  if (!db) throw new Error("Database not available");

  // Vérifier que le support appartient bien au formateur
  const [support] = await db
    .select()
    .from(supportsFormation)
    .where(
      and(
        eq(supportsFormation.id, supportId),
        eq(supportsFormation.formateurId, formateurId)
      )
    )
    .limit(1);

  if (!support) {
    throw new Error("Support non trouvé ou non autorisé");
  }

  await db.delete(supportsFormation).where(eq(supportsFormation.id, supportId));
  
  return support; // Retourner le support pour récupérer la clé S3
}

/**
 * Valide la présence d'un apprenant
 * @param inscriptionId - ID de l'inscription
 * @param present - true si présent, false sinon
 */
export async function validerPresence(inscriptionId: number, present: boolean) {
  const db = await getDb();
  if (!db) throw new Error("Database not available");

  await db
    .update(inscriptions)
    .set({ statut: present ? 'confirmee' : 'annulee' })
    .where(eq(inscriptions.id, inscriptionId));
}

/**
 * Récupère l'historique des formations d'un formateur
 * @param formateurId - ID du formateur
 * @param limit - Nombre de résultats à retourner (par défaut 50)
 */
export async function getHistoriqueFormateur(formateurId: number, limit: number = 50) {
  const db = await getDb();
  if (!db) return [];

  const results = await db
    .select({
      sequenceId: sequences.id,
      sequenceNom: sequences.nom,
      formationNom: formations.nom,
      dateDebut: sql<Date>`MIN(${datesFormation.dateDebut})`,
      dateFin: sql<Date>`MAX(${datesFormation.dateFin})`,
      lieu: sequences.lieu,
      nbInscrits: sql<number>`(
        SELECT COUNT(*) 
        FROM inscriptions 
        WHERE inscriptions.sequenceId = ${sequences.id}
        AND inscriptions.statut IN ('confirmee', 'liste_attente')
      )`,
      nbPresents: sql<number>`(
        SELECT COUNT(*) 
        FROM inscriptions 
        WHERE inscriptions.sequenceId = ${sequences.id}
        AND inscriptions.statut = 'confirmee'
      )`,
    })
    .from(sequences)
    .innerJoin(formations, eq(sequences.formationId, formations.id))
    .innerJoin(datesFormation, eq(datesFormation.sequenceId, sequences.id))
    .where(eq(sequences.formateurId, formateurId))
    .groupBy(sequences.id, sequences.nom, formations.nom, sequences.lieu)
    .orderBy(desc(sql`MIN(${datesFormation.dateDebut})`))
    .limit(limit);

  return results;
}

/**
 * Récupère les détails d'une séquence pour un formateur
 * @param sequenceId - ID de la séquence
 * @param formateurId - ID du formateur (pour vérification)
 */
export async function getDetailSequence(sequenceId: number, formateurId: number) {
  const db = await getDb();
  if (!db) return null;

  const [result] = await db
    .select({
      id: sequences.id,
      nom: sequences.nom,
      formationNom: formations.nom,
      lieu: sequences.lieu,
      capaciteMax: sequences.capaciteMax,
      formateurNom: formateurs.nom,
    })
    .from(sequences)
    .innerJoin(formations, eq(sequences.formationId, formations.id))
    .innerJoin(formateurs, eq(sequences.formateurId, formateurs.id))
    .where(
      and(
        eq(sequences.id, sequenceId),
        eq(sequences.formateurId, formateurId)
      )
    )
    .limit(1);

  if (!result) return null;

  // Récupérer les dates de formation
  const dates = await db
    .select({
      id: datesFormation.id,
      dateDebut: datesFormation.dateDebut,
      dateFin: datesFormation.dateFin,
      ordre: datesFormation.ordre,
    })
    .from(datesFormation)
    .where(eq(datesFormation.sequenceId, sequenceId))
    .orderBy(datesFormation.dateDebut);

  return {
    ...result,
    dates,
  };
}

/**
 * Récupérer les statistiques du tableau de bord pour un formateur
 */
export async function getFormateurDashboardStats(formateurId: number) {
  const db = await getDb();
  if (!db) throw new Error("Database not available");

  // Nombre total de séquences du formateur
  const totalSequences = await db
    .select({ count: sql<number>`COUNT(*)` })
    .from(sequences)
    .where(eq(sequences.formateurId, formateurId));

  // Nombre total d'inscrits confirmés à toutes les séquences du formateur
  const totalInscrits = await db
    .select({ count: sql<number>`COUNT(*)` })
    .from(inscriptions)
    .innerJoin(sequences, eq(inscriptions.sequenceId, sequences.id))
    .where(
      and(
        eq(sequences.formateurId, formateurId),
        eq(inscriptions.statut, 'confirmee')
      )
    );

  // Nombre de séquences à venir (ayant au moins une date future)
  const today = new Date();
  const sequencesAvenir = await db
    .select({ sequenceId: datesFormation.sequenceId })
    .from(datesFormation)
    .innerJoin(sequences, eq(datesFormation.sequenceId, sequences.id))
    .where(
      and(
        eq(sequences.formateurId, formateurId),
        gte(datesFormation.dateDebut, today)
      )
    )
    .groupBy(datesFormation.sequenceId);

  return {
    totalSequences: totalSequences[0]?.count || 0,
    totalInscrits: totalInscrits[0]?.count || 0,
    sequencesAvenir: sequencesAvenir.length,
  };
}

/**
 * Récupérer les prochaines séquences du formateur avec leurs détails
 */
export async function getFormateurProchainesSequences(formateurId: number, limit: number = 10) {
  const db = await getDb();
  if (!db) return [];

  const today = new Date();

  // Récupérer les séquences du formateur ayant au moins une date future
  const seqs = await db
    .select()
    .from(sequences)
    .where(eq(sequences.formateurId, formateurId));

  // Pour chaque séquence, récupérer les détails
  const sequencesAvecDetails = await Promise.all(
    seqs.map(async (seq) => {
      // Récupérer toutes les dates de la séquence
      const dates = await db
        .select()
        .from(datesFormation)
        .where(eq(datesFormation.sequenceId, seq.id))
        .orderBy(datesFormation.dateDebut);

      // Vérifier si au moins une date est future
      const hasFutureDate = dates.some(d => new Date(d.dateDebut) >= today);
      
      if (!hasFutureDate) return null;

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

      // Compter les inscrits confirmés
      const inscritsCount = await db
        .select({ count: sql<number>`COUNT(*)` })
        .from(inscriptions)
        .where(
          and(
            eq(inscriptions.sequenceId, seq.id),
            eq(inscriptions.statut, 'confirmee')
          )
        );

      return {
        ...seq,
        formation: formation[0] || null,
        dates,
        nbInscrits: inscritsCount[0]?.count || 0,
        prochaineDateDebut: dates.find(d => new Date(d.dateDebut) >= today)?.dateDebut || dates[0]?.dateDebut,
      };
    })
  );

  // Filtrer les séquences nulles et trier par prochaine date
  const sequencesFiltrees = sequencesAvecDetails
    .filter(s => s !== null)
    .sort((a, b) => {
      const dateA = new Date(a!.prochaineDateDebut);
      const dateB = new Date(b!.prochaineDateDebut);
      return dateA.getTime() - dateB.getTime();
    })
    .slice(0, limit);

  return sequencesFiltrees;
}

/**
 * Récupérer l'email du formateur à partir de son ID
 */
export async function getFormateurEmailById(formateurId: number): Promise<string | null> {
  const db = await getDb();
  if (!db) return null;

  const result = await db
    .select({ email: formateurs.email })
    .from(formateurs)
    .where(eq(formateurs.id, formateurId))
    .limit(1);

  return result[0]?.email || null;
}
