/**
 * Utilidades puras para rango de registros y tiempo relativo (último acceso).
 * Extraídas de Tabla.jsx para tests y reutilización sin acoplar al componente.
 */

/**
 * Parsea una fecha que puede ser string (ISO, DD/MM/YYYY) o Date y devuelve Date o null.
 * @param {unknown} value
 * @returns {Date | null}
 */
function parseFechaUltimoAcceso(value) {
  if (value == null || value === '') return null;
  if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
  const str = String(value).trim();
  if (!str) return null;
  const iso = new Date(str);
  if (!Number.isNaN(iso.getTime())) return iso;
  const match = str.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})/);
  if (match) {
    const [, d, m, y] = match;
    const dNum = parseInt(d, 10);
    const mNum = parseInt(m, 10) - 1;
    const yNum = parseInt(y, 10);
    const parsed = new Date(yNum, mNum, dNum);
    if (!Number.isNaN(parsed.getTime())) return parsed;
  }
  return null;
}

/**
 * Tiempo transcurrido en español: "Hace un momento", "Hace 15 horas", "Ayer", etc.
 * @param {unknown} fecha
 * @returns {string}
 */
export function tiempoRelativoEnEspanol(fecha) {
  const date = parseFechaUltimoAcceso(fecha);
  if (!date) return '—';
  const now = new Date();
  const diffMs = now - date;
  const diffSeg = Math.floor(diffMs / 1000);
  const diffMin = Math.floor(diffSeg / 60);
  const diffHoras = Math.floor(diffMin / 60);
  const diffDias = Math.floor(diffHoras / 24);

  if (diffSeg < 60) return 'Hace un momento';
  if (diffMin < 60) return diffMin === 1 ? 'Hace 1 minuto' : `Hace ${diffMin} minutos`;
  if (diffHoras < 24) return diffHoras === 1 ? 'Hace 1 hora' : `Hace ${diffHoras} horas`;
  if (diffDias === 1) return 'Ayer';
  if (diffDias < 7) return `Hace ${diffDias} días`;
  const semanas = Math.floor(diffDias / 7);
  if (semanas === 1) return 'Hace una semana';
  if (semanas < 4) return `Hace ${semanas} semanas`;
  const meses = Math.floor(diffDias / 30);
  if (meses === 1) return 'Hace un mes';
  if (meses < 12) return `Hace ${meses} meses`;
  const anios = Math.floor(diffDias / 365);
  return anios === 1 ? 'Hace un año' : `Hace ${anios} años`;
}

/**
 * Índices 1-based del rango visible (cabeceras / lógica); el texto en barra es “Registros: N de total”.
 * @param {object} params
 * @param {boolean} params.sinContenido
 * @param {number} params.totalRegistrosLabel
 * @param {number} params.registrosEnPagina
 * @param {number} params.currentPage
 * @param {number} params.totalPages
 * @returns {{ desde: number, hasta: number }}
 */
export function computeRegistrosRangoDesdeHasta({
  sinContenido,
  totalRegistrosLabel,
  registrosEnPagina,
  currentPage,
  totalPages,
}) {
  const total = totalRegistrosLabel;
  const n = registrosEnPagina;
  if (sinContenido || total === 0) {
    return { desde: 0, hasta: 0 };
  }
  if (n === 0) {
    return { desde: 0, hasta: 0 };
  }
  const tp = Math.max(1, totalPages || 1);
  const pageSize = Math.ceil(total / tp);
  const desde = (currentPage - 1) * pageSize + 1;
  const hasta = Math.min(total, desde + n - 1);
  return { desde, hasta };
}

/**
 * Índices 1-based cuando el tamaño de página es explícito (registros por página fijos).
 * @param {object} params
 * @param {boolean} params.sinContenido
 * @param {number} params.totalRegistrosLabel
 * @param {number} params.registrosEnPagina
 * @param {number} params.currentPage
 * @param {number} params.pageSize
 * @returns {{ desde: number, hasta: number }}
 */
export function computeRegistrosRangoDesdeHastaConPageSize({
  sinContenido,
  totalRegistrosLabel,
  registrosEnPagina,
  currentPage,
  pageSize,
}) {
  const total = totalRegistrosLabel;
  const n = registrosEnPagina;
  const ps = Math.max(1, Math.floor(Number(pageSize)) || 1);
  if (sinContenido || total === 0) {
    return { desde: 0, hasta: 0 };
  }
  if (n === 0) {
    return { desde: 0, hasta: 0 };
  }
  const desde = (Math.max(1, currentPage) - 1) * ps + 1;
  const hasta = Math.min(total, desde + n - 1);
  return { desde, hasta };
}

/**
 * Límites del número editable de **registros por página**:
 * - Si `total >= 10`: entre 10 y `min(100, total)`.
 * - Si `total < 10`: entre 1 y `total` (sin subir a 10).
 * @param {number} value
 * @param {number} totalRegistrosLabel
 * @returns {number}
 */
export function clampRegistrosPorPagina(value, totalRegistrosLabel) {
  const total = Math.max(0, Math.floor(Number(totalRegistrosLabel)) || 0);
  if (total === 0) return 0;
  const maxPorPagina = Math.min(100, total);
  const minPorPagina = total < 10 ? 1 : Math.min(10, maxPorPagina);
  const n = Math.floor(Number(value));
  if (!Number.isFinite(n)) return minPorPagina;
  return Math.max(minPorPagina, Math.min(maxPorPagina, n));
}

/** @deprecated Usar `clampRegistrosPorPagina` (misma función). */
export const clampRegistrosHastaEditable = clampRegistrosPorPagina;

/**
 * Tras cambiar el tamaño de página, página1-based que mantiene el primer registro visible (o la más cercana).
 * @param {object} p
 * @param {number} p.currentPage
 * @param {number} p.previousPageSize
 * @param {number} p.nextPageSize
 * @param {number} p.totalRegistrosLabel
 * @returns {number}
 */
export function resolvePageAfterPageSizeChange({
  currentPage,
  previousPageSize,
  nextPageSize,
  totalRegistrosLabel,
}) {
  const total = Math.max(0, Math.floor(Number(totalRegistrosLabel)) || 0);
  const psPrev = Math.max(1, Math.floor(Number(previousPageSize)) || 1);
  const psNext = Math.max(1, Math.floor(Number(nextPageSize)) || 1);
  if (total === 0) return 1;
  const newTotalPages = Math.max(1, Math.ceil(total / psNext));
  const firstIndex = (Math.max(1, currentPage) - 1) * psPrev + 1;
  const newPage = Math.min(newTotalPages, Math.max(1, Math.ceil(firstIndex / psNext)));
  return newPage;
}

