/**
 * Utilitaires centralisés pour la gestion des dates
 * Regroupe toutes les fonctions de formatage, manipulation et validation de dates
 */

import {
  format,
  startOfDay,
  endOfDay,
  isValid,
  formatDistanceToNow as formatDistanceToNowFns,
} from 'date-fns';
import { fr } from 'date-fns/locale';

// Re-export des utilitaires workday existants
export {
  getPreviousWorkday,
  getNextWorkday,
  isWorkday,
  getDayName,
} from './workday-utils';

/**
 * Formats de dates standardisés
 */
export const DATE_FORMATS = {
  API: 'yyyy-MM-dd', // Format API (YYYY-MM-DD)
  DISPLAY_SHORT: 'dd/MM/yy', // Format court (01/12/25)
  DISPLAY_LONG: 'EEEE d MMMM yyyy', // Format long (lundi 1 décembre 2025)
  DISPLAY_MEDIUM: 'dd/MM/yyyy', // Format moyen (01/12/2025)
  ISO: "yyyy-MM-dd'T'HH:mm:ss.SSSxxx", // Format ISO complet
} as const;

/**
 * Normalise une date au début de la journée (00:00:00.000)
 */
export function normalizeDate(date: Date): Date {
  const normalized = new Date(date);
  normalized.setHours(0, 0, 0, 0);
  return normalized;
}

/**
 * Formate une date pour l'API (évite les décalages timezone)
 * @param date - Date à formater
 * @returns Format YYYY-MM-DD
 */
export function formatDateForAPI(date: Date | string): string {
  const ensuredDate = ensureDate(date);
  if (!ensuredDate || !isValid(ensuredDate)) {
    throw new Error('Date invalide fournie à formatDateForAPI');
  }

  const year = ensuredDate.getFullYear();
  const month = String(ensuredDate.getMonth() + 1).padStart(2, '0');
  const day = String(ensuredDate.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}

/**
 * Formate une date pour l'affichage en français
 */
export function formatDateForDisplay(
  date: Date | string,
  formatType: keyof typeof DATE_FORMATS = 'DISPLAY_MEDIUM'
): string {
  const ensuredDate = ensureDate(date);
  if (!ensuredDate || !isValid(ensuredDate)) {
    throw new Error('Date invalide fournie à formatDateForDisplay');
  }

  return format(ensuredDate, DATE_FORMATS[formatType], { locale: fr });
}

/**
 * Formate une date courte pour l'affichage (dd/MM/yy)
 */
export function formatDateShort(date: Date): string {
  return formatDateForDisplay(date, 'DISPLAY_SHORT');
}

/**
 * Calcule le nombre de jours écoulés depuis une date
 */
export function getDaysAgo(date: Date | string): number {
  const ensuredDate = ensureDate(date);
  if (!ensuredDate) {
    throw new Error('Date invalide fournie à getDaysAgo');
  }

  const today = getToday();
  const diffTime = today.getTime() - normalizeDate(ensuredDate).getTime();
  return Math.floor(diffTime / (1000 * 60 * 60 * 24));
}

/**
 * Formate une date longue pour l'affichage (lundi 1 décembre 2025)
 */
export function formatDateLong(date: Date): string {
  return formatDateForDisplay(date, 'DISPLAY_LONG');
}

/**
 * Vérifie si une date est aujourd'hui
 */
export function isToday(date: Date | string): boolean {
  const ensuredDate = ensureDate(date);
  if (!ensuredDate) return false;

  const today = new Date();
  return (
    normalizeDate(ensuredDate).getTime() === normalizeDate(today).getTime()
  );
}

/**
 * Vérifie si une date est hier
 */
export function isYesterday(date: Date | string): boolean {
  const ensuredDate = ensureDate(date);
  if (!ensuredDate) return false;

  const yesterday = new Date();
  yesterday.setDate(yesterday.getDate() - 1);
  return (
    normalizeDate(ensuredDate).getTime() === normalizeDate(yesterday).getTime()
  );
}

/**
 * Compare deux dates (sans tenir compte de l'heure)
 */
export function isSameDay(date1: Date | string, date2: Date | string): boolean {
  const ensuredDate1 = ensureDate(date1);
  const ensuredDate2 = ensureDate(date2);

  if (!ensuredDate1 || !ensuredDate2) return false;

  return (
    normalizeDate(ensuredDate1).getTime() ===
    normalizeDate(ensuredDate2).getTime()
  );
}

/**
 * Obtient la date d'aujourd'hui normalisée
 */
export function getToday(): Date {
  return normalizeDate(new Date());
}

/**
 * Obtient la date d'hier normalisée
 */
export function getYesterday(): Date {
  const yesterday = new Date();
  yesterday.setDate(yesterday.getDate() - 1);
  return normalizeDate(yesterday);
}

/**
 * Crée une nouvelle date à partir d'une date existante
 */
export function createDate(date: Date): Date {
  return new Date(date);
}

/**
 * Crée une date à partir de composants année/mois/jour
 */
export function createDateFromParts(
  year: number,
  month: number,
  day: number
): Date {
  return new Date(year, month - 1, day); // month est 0-indexé en JavaScript
}

/**
 * Convertit une date pour un input datetime-local (gestion timezone)
 */
export function formatDateForDateTimeInput(date: Date): string {
  const adjustedDate = new Date(
    date.getTime() - date.getTimezoneOffset() * 60000
  );
  return adjustedDate.toISOString().slice(0, 16);
}

/**
 * Parse une valeur d'input datetime-local vers une Date
 */
export function parseDateTimeInput(value: string): Date {
  return new Date(value);
}

/**
 * Ajoute des jours à une date
 */
export function addDays(date: Date, days: number): Date {
  const result = createDate(date);
  result.setDate(result.getDate() + days);
  return result;
}

/**
 * Ajoute des minutes à une date
 */
export function addMinutes(date: Date, minutes: number): Date {
  const result = createDate(date);
  result.setMinutes(result.getMinutes() + minutes);
  return result;
}

/**
 * Soustrait des jours à une date
 */
export function subtractDays(date: Date, days: number): Date {
  return addDays(date, -days);
}

/**
 * Parse une date depuis une string avec validation
 */
export function parseDate(dateString: string): Date {
  const parsed = new Date(dateString);
  if (!isValid(parsed)) {
    throw new Error(`Date invalide: ${dateString}`);
  }
  return parsed;
}

/**
 * Valide qu'une string est une date valide au format API (YYYY-MM-DD)
 */
export function isValidAPIDate(dateString: string): boolean {
  const regex = /^\d{4}-\d{2}-\d{2}$/;
  if (!regex.test(dateString)) {
    return false;
  }

  try {
    const date = parseDate(dateString);
    return formatDateForAPI(date) === dateString;
  } catch {
    return false;
  }
}

/**
 * Obtient le début de la journée pour une date
 */
export function getStartOfDay(date: Date): Date {
  return startOfDay(date);
}

/**
 * Obtient la fin de la journée pour une date
 */
export function getEndOfDay(date: Date): Date {
  return endOfDay(date);
}

/**
 * Formate une date pour l'affichage avec gestion des cas spéciaux (aujourd'hui, hier)
 */
export function formatDateSmart(date: Date): string {
  if (isToday(date)) {
    return "Aujourd'hui";
  }

  if (isYesterday(date)) {
    return 'Hier';
  }

  return formatDateForDisplay(date, 'DISPLAY_MEDIUM');
}

/**
 * Génère un titre intelligent pour une date (avec emojis)
 */
export function generateDateTitle(
  date: Date,
  emoji: string = '📅'
): { emoji: string; text: string } {
  if (isToday(date)) {
    return { emoji, text: "Aujourd'hui" };
  }

  if (isYesterday(date)) {
    return { emoji, text: 'Hier' };
  }

  return { emoji, text: formatDateShort(date) };
}

/**
 * Formate la distance depuis maintenant en français
 */
export function formatDistanceToNow(
  date: Date,
  options?: { addSuffix?: boolean }
): string {
  if (!isValid(date)) {
    throw new Error('Date invalide fournie à formatDistanceToNow');
  }

  return formatDistanceToNowFns(date, {
    locale: fr,
    addSuffix: options?.addSuffix ?? true,
  });
}

/**
 * S'assure qu'une valeur est un objet Date valide
 * Convertit les chaînes de caractères en Date si nécessaire
 */
export function ensureDate(
  value: Date | string | null | undefined
): Date | null {
  if (value === null || value === undefined) {
    return null;
  }

  // Si c'est déjà un objet Date valide
  if (value instanceof Date && isValid(value)) {
    return value;
  }

  // Si c'est une chaîne de caractères, essayer de la parser
  if (typeof value === 'string') {
    try {
      const parsed = parseDate(value);
      return parsed;
    } catch {
      console.warn(`Impossible de parser la date: ${value}`);
      return null;
    }
  }

  // Si c'est un objet Date mais invalide, essayer de le recréer
  if (value instanceof Date) {
    try {
      const time = value.getTime();
      if (!isNaN(time)) {
        return value;
      }
    } catch {
      console.warn('Objet Date invalide détecté');
    }
  }

  console.warn('Valeur de date non supportée:', value);
  return null;
}

/**
 * S'assure qu'une valeur est un objet Date valide, avec une valeur par défaut
 */
export function ensureDateWithDefault(
  value: Date | string | null | undefined,
  defaultValue: Date = new Date()
): Date {
  const result = ensureDate(value);
  return result || defaultValue;
}
