/**
 * dateFormatter.ts
 * 
 * Utilities pour parser, formatter et grouper les dates de travail
 * Format input/storage: "12/10, 13/10, 14/10, 24/10"
 * Format affichage: "le 12/10 ; du 14/10 au 17/10."
 */

// Types
export interface DateGroup {
  type: "single" | "range";
  displayFr: string; // "le 12/10" ou "du 14/10 au 17/10"
  startIso: string;  // "2025-10-14"
  endIso?: string;   // "2025-10-17"
}

export interface ParseResult {
  groups: DateGroup[];
  hasMultiMonth: boolean;
  pdfFormatted: string;
  allDatesFr: string[]; // Pour affichage inline: ["12/10", "13/10", ...]
}

/**
 * Formate une date ISO (YYYY-MM-DD) en format français (DD/MM)
 */
export function formatDateFr(isoStr: string): string {
  if (!isoStr || isoStr.length < 10) return "";
  const [year, month, day] = isoStr.split("-");
  return `${day}/${month}`;
}

/**
 * Convertit une date française (DD/MM) en ISO (YYYY-MM-DD)
 * Nécessite l'année de contexte
 */
export function parseFrenchedDate(frStr: string, yearContext: string): string {
  const cleaned = frStr.trim();
  if (!cleaned || !cleaned.includes("/")) return "";
  
  const [day, month] = cleaned.split("/");
  if (!day || !month) return "";
  
  const year = yearContext.split("-")[0];
  return `${year}-${month.padStart(2, "0")}-${day.padStart(2, "0")}`;
}

/**
 * Vérifie si deux dates ISO consécutives sont à exactement 1 jour d'intervalle
 */
function isConsecutiveDay(isoStr1: string, isoStr2: string): boolean {
  const date1 = new Date(isoStr1 + "T00:00:00Z");
  const date2 = new Date(isoStr2 + "T00:00:00Z");
  const diffMs = date2.getTime() - date1.getTime();
  const diffDays = diffMs / (1000 * 60 * 60 * 24);
  return Math.abs(diffDays - 1) < 0.01; // tolérance pour fuseaux horaires
}

/**
 * Génère toutes les dates ISO entre startIso et endIso (inclus)
 */
function generateDateRange(startIso: string, endIso: string): string[] {
  const dates: string[] = [];
  const current = new Date(startIso + "T00:00:00Z");
  const end = new Date(endIso + "T00:00:00Z");
  
  while (current <= end) {
    dates.push(current.toISOString().slice(0, 10));
    current.setUTCDate(current.getUTCDate() + 1);
  }
  
  return dates;
}

// Exporter generateDateRange pour usage externe
export { generateDateRange };

/**
 * Format des dates quantifiées.
 * Prend une liste de dates ISO (YYYY-MM-DD) et une map { iso: quantity }
 * Retourne une chaîne formatée pour PDFMonkey en regroupant les dates
 * consécutives qui ont la même quantité. Exemple:
 * "1 représentation le 12/10 ; 1 représentation par jour du 13/10 au 16/10 ; 3 heures le 13/10."
 */
export function formatQuantifiedDates(
  isos: string[],
  quantities: Record<string, number | string>,
  dateType: "representations" | "repetitions" | "jours_travail" | "heures_repetitions",
  repetitionDuration?: "3" | "4"
): string {
  if (!isos || isos.length === 0) return "";

  // Trier les dates
  const sorted = Array.from(new Set(isos)).sort();

  // Construire tableau { iso, qty }
  const items = sorted.map((iso) => ({ iso, qty: quantities[iso] ?? quantities[formatDateFr(iso)] ?? 0 }));

  // Grouper consécutifs avec même qty
  const groups: Array<{ startIso: string; endIso?: string; qty: number | string } > = [];
  for (let i = 0; i < items.length; i++) {
    const cur = items[i];
    const prev = groups[groups.length - 1];
    if (!prev) {
      groups.push({ startIso: cur.iso, endIso: cur.iso, qty: cur.qty });
      continue;
    }
    const isConsec = isConsecutiveDay(prev.endIso!, cur.iso);
    const sameQty = String(prev.qty) === String(cur.qty);
    if (isConsec && sameQty) {
      prev.endIso = cur.iso;
    } else {
      groups.push({ startIso: cur.iso, endIso: cur.iso, qty: cur.qty });
    }
  }

  // Construire le texte
  const parts: string[] = groups.map((g) => {
    const qty = g.qty;
    const qtyNum = typeof qty === 'number' ? qty : (parseInt(String(qty)) || 0);
    
    // déterminer le libellé de base
    let base = '';
    if (dateType === 'representations') {
      base = qtyNum > 1 ? 'représentations' : 'représentation';
    } else if (dateType === 'repetitions') {
      // Pour les répétitions, ajouter la durée si disponible
      if (repetitionDuration) {
        if (qtyNum > 1) {
          base = `services de répétition de ${repetitionDuration} heures chacun`;
        } else {
          base = `service de répétition de ${repetitionDuration} heures`;
        }
      } else {
        base = qtyNum > 1 ? 'services de répétition' : 'service de répétition';
      }
    } else if (dateType === 'jours_travail' || dateType === 'heures_repetitions') {
      base = qtyNum > 1 ? 'heures' : 'heure';
    }

    if (g.startIso === g.endIso) {
      // date isolée
      return `${qty} ${base} le ${formatDateFr(g.startIso)}`;
    }

    // plage: utiliser "par jour" pour indiquer par-journée
    if (dateType === 'jours_travail' || dateType === 'heures_repetitions') {
      return `${qty} ${base} par jour du ${formatDateFr(g.startIso)} au ${formatDateFr(g.endIso!)}`;
    }

    // representations / repetitions -> "X base par jour du A au B"
    return `${qty} ${base} par jour du ${formatDateFr(g.startIso)} au ${formatDateFr(g.endIso!)}`;
  });

  return parts.join(' ; ') + '.';
}

/**
 * Parse une chaîne de dates en français (DD/MM) séparées par des virgules
 * Retourne des groupes (dates isolées ou plages consécutives)
 * 
 * @param dateStr ex: "12/10, 13/10, 14/10, 24/10"
 * @param yearContext ex: "2025-10-12" (pour extraire l'année)
 * @returns ParseResult avec groupes, détection multi-mois et formatages
 */
export function parseDateString(
  dateStr: string,
  yearContext: string
): ParseResult {
  if (!dateStr || !yearContext) {
    return {
      groups: [],
      hasMultiMonth: false,
      pdfFormatted: "",
      allDatesFr: [],
    };
  }

  // 1. Parser chaque date en ISO
  const isoArray = dateStr
    .split(",")
    .map(d => d.trim())
    .filter(Boolean)
    .map(d => parseFrenchedDate(d, yearContext))
    .filter(iso => iso.length === 10) // Valider format YYYY-MM-DD
    .sort(); // Trier chronologiquement

  // Dédupliquer
  const uniqueIsos = [...new Set(isoArray)];

  if (uniqueIsos.length === 0) {
    return {
      groups: [],
      hasMultiMonth: false,
      pdfFormatted: "",
      allDatesFr: [],
    };
  }

  // 2. Grouper les dates consécutives
  const groups: DateGroup[] = [];
  let i = 0;
  while (i < uniqueIsos.length) {
    const startIso = uniqueIsos[i];
    let endIso = startIso;

    // Trouver la fin de la plage consécutive
    while (i + 1 < uniqueIsos.length && isConsecutiveDay(uniqueIsos[i], uniqueIsos[i + 1])) {
      endIso = uniqueIsos[i + 1];
      i++;
    }

    // Créer le groupe
    if (startIso === endIso) {
      groups.push({
        type: "single",
        displayFr: `le ${formatDateFr(startIso)}`,
        startIso,
      });
    } else {
      groups.push({
        type: "range",
        displayFr: `du ${formatDateFr(startIso)} au ${formatDateFr(endIso)}`,
        startIso,
        endIso,
      });
    }

    i++;
  }

  // 3. Formater pour PDFMonkey
  const pdfFormatted = groups.map(g => g.displayFr).join(" ; ") + ".";

  // 4. Déterminer multi-mois
  const months = new Set(uniqueIsos.map(iso => iso.slice(0, 7))); // ["2025-10", "2025-11", ...]
  const hasMultiMonth = months.size > 1;

  // 5. Générer array de toutes les dates en français
  const allDatesFr = uniqueIsos.map(iso => formatDateFr(iso));

  return {
    groups,
    hasMultiMonth,
    pdfFormatted,
    allDatesFr,
  };
}

/**
 * Convertit les groupes en format input/storage (pour Supabase)
 * Ex: ["12/10", "13/10", "14/10", "24/10"]
 */
export function formatGroupsToInput(groups: DateGroup[]): string {
  const allDates: string[] = [];

  for (const group of groups) {
    if (group.type === "single") {
      allDates.push(formatDateFr(group.startIso));
    } else if (group.type === "range" && group.endIso) {
      // Générer toutes les dates dans la plage
      const rangeDates = generateDateRange(group.startIso, group.endIso);
      allDates.push(...rangeDates.map(iso => formatDateFr(iso)));
    }
  }

  return allDates.join(", ");
}

/**
 * Convertit les groupes en format affichage lisible pour PDFMonkey
 * Ex: "le 12/10 ; du 13/10 au 14/10 ; le 24/10."
 */
export function formatGroupsToPDFMonkey(groups: DateGroup[]): string {
  if (groups.length === 0) return "";
  return groups.map(g => g.displayFr).join(" ; ") + ".";
}

/**
 * Convertit un array de dates ISO en DateGroup[]
 * Utile pour le calendrier qui sélectionne les dates en ISO
 */
export function convertIsoDatesToGroups(isoDates: string[]): DateGroup[] {
  if (!isoDates || isoDates.length === 0) return [];

  const sorted = [...isoDates].sort();
  const groups: DateGroup[] = [];
  let i = 0;

  while (i < sorted.length) {
    const startIso = sorted[i];
    let endIso = startIso;

    // Trouver la fin de la plage consécutive
    while (i + 1 < sorted.length && isConsecutiveDay(sorted[i], sorted[i + 1])) {
      endIso = sorted[i + 1];
      i++;
    }

    // Créer le groupe
    if (startIso === endIso) {
      groups.push({
        type: "single",
        displayFr: `le ${formatDateFr(startIso)}`,
        startIso,
      });
    } else {
      groups.push({
        type: "range",
        displayFr: `du ${formatDateFr(startIso)} au ${formatDateFr(endIso)}`,
        startIso,
        endIso,
      });
    }

    i++;
  }

  return groups;
}

/**
 * Détecte si les dates couvrent plusieurs mois
 */
export function hasMultipleMoons(isoDates: string[]): boolean {
  if (!isoDates || isoDates.length === 0) return false;
  const months = new Set(isoDates.map(iso => iso.slice(0, 7)));
  return months.size > 1;
}

/**
 * Récupère le mois/année d'une date ISO
 * Ex: "2025-10-12" → "2025-10"
 */
export function getMonthYear(isoStr: string): string {
  return isoStr.slice(0, 7);
}

/**
 * Récupère toutes les dates d'une plage consécutive (pour le calendrier)
 */
export function expandDateRange(startIso: string, endIso: string): string[] {
  return generateDateRange(startIso, endIso);
}
