/**
 * Aritmética de fechas sin año (ticket CRM-20). Pura y sin dependencias: es lo
 * único que decide si hoy toca avisar, y equivocarse acá significa saludar a un
 * cliente el día después de su cumpleaños.
 */

export interface FechaRecordable {
  dia: number;
  mes: number;
  recordar_dias_antes: number;
  ultimo_aviso_anio?: number | null;
}

/** Medianoche local, para comparar días completos sin que la hora ensucie la cuenta. */
export function soloDia(fecha: Date): Date {
  return new Date(fecha.getFullYear(), fecha.getMonth(), fecha.getDate());
}

/**
 * La próxima vez que cae (dia, mes) a partir de hoy, hoy incluido.
 *
 * El 29 de febrero sólo existe un año de cada cuatro. En vez de saltearlo —que
 * dejaría a esa persona sin saludo tres años seguidos— se corre al 1 de marzo,
 * que es el criterio que usa el registro civil paraguayo para los vencimientos.
 */
export function proximaOcurrencia(dia: number, mes: number, hoy: Date): Date {
  const base = soloDia(hoy);
  for (const anio of [base.getFullYear(), base.getFullYear() + 1]) {
    const fecha = fechaDeAnio(dia, mes, anio);
    if (fecha >= base) return fecha;
  }
  // Inalcanzable: el año siguiente siempre queda adelante.
  return fechaDeAnio(dia, mes, base.getFullYear() + 1);
}

function fechaDeAnio(dia: number, mes: number, anio: number): Date {
  const fecha = new Date(anio, mes - 1, dia);
  // new Date(2027, 1, 29) rueda al 1 de marzo solo: justo lo que queremos.
  return fecha;
}

/** Días completos entre hoy y la fecha. 0 = es hoy. */
export function diasHasta(dia: number, mes: number, hoy: Date): number {
  const objetivo = proximaOcurrencia(dia, mes, hoy);
  const ms = objetivo.getTime() - soloDia(hoy).getTime();
  return Math.round(ms / 86_400_000);
}

/**
 * Si toca generar el aviso hoy.
 *
 * `>=` y no `===` a propósito: si el job no corrió por un día —deploy, servidor
 * caído— al día siguiente el aviso igual sale, sólo con menos anticipación.
 * Con `===` esa fecha se perdía hasta el año que viene.
 *
 * `ultimo_aviso_anio` es lo que evita que vuelva a salir todos los días hasta la
 * fecha: se compara contra el año de la ocurrencia, no contra el año de hoy,
 * porque un aviso de Navidad con 14 días de anticipación puede caer en diciembre
 * y uno de Año Nuevo, en diciembre del año anterior.
 */
export function debeAvisar(fecha: FechaRecordable, hoy: Date = new Date()): boolean {
  const dias = diasHasta(fecha.dia, fecha.mes, hoy);
  if (dias > fecha.recordar_dias_antes) return false;
  const anioObjetivo = proximaOcurrencia(fecha.dia, fecha.mes, hoy).getFullYear();
  return fecha.ultimo_aviso_anio !== anioObjetivo;
}

/** Texto para el asunto de la actividad: "hoy", "mañana" o "en N días". */
export function cuandoEnPalabras(dias: number): string {
  if (dias <= 0) return 'hoy';
  if (dias === 1) return 'mañana';
  return `en ${dias} días`;
}
