// JUNTAR LOS AVISOS QUE LLEGAN EN RAFAGA.
//
// Las pantallas que viven en tiempo real (Pedidos, Cocina) traian del servidor
// el pedido que cambio, de UNO EN UNO, en el momento en que llegaba el aviso.
// Con un cambio suelto va perfecto; el problema es la rafaga: veinte cambios
// seguidos disparaban veinte consultas a la vez, y ahi el navegador se queda
// sin recursos (ERR_INSUFFICIENT_RESOURCES) y deja de recibir TODO, incluida la
// conexion de tiempo real. O sea que la pantalla se congela justo cuando mas
// se mueve el negocio, que es cuando menos puede.
//
// Esto junta los avisos de un mismo respiro y hace UNA sola consulta con todos.
import { useCallback, useEffect, useRef } from 'react';

/** Cuantos se piden como maximo en una sola consulta (la direccion tiene tope). */
const TOPE_POR_TANDA = 40;

/**
 * Devuelve una funcion `pedir(id)` que NO consulta de inmediato: apunta el id y
 * espera un respiro por si vienen mas. Cumplido el respiro, llama a `traer` con
 * todos los ids juntos y le pasa el resultado a `aplicar`.
 *
 * `traer` y `aplicar` pueden cambiar en cada render sin reiniciar nada: se leen
 * por referencia, igual que en useCambiosRestaurante.
 */
export function useTraidaAgrupada<T>(
  traer: (ids: string[]) => Promise<T[]>,
  aplicar: (filas: T[], ids: string[]) => void,
  respiroMs = 250,
): (id: string) => void {
  const pendientes = useRef<Set<string>>(new Set());
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const vivo = useRef(true);
  const traerRef = useRef(traer);
  const aplicarRef = useRef(aplicar);
  traerRef.current = traer;
  aplicarRef.current = aplicar;

  useEffect(() => {
    vivo.current = true;
    // Se toman las cajas AQUI (no dentro del cierre) porque son las mismas
    // durante toda la vida del componente y asi el linter no supone que
    // cambiaron para cuando toque limpiar.
    const cajaPendientes = pendientes.current;
    const cajaVivo = vivo;
    const cajaTimer = timer;
    return () => {
      // Al salir de la pantalla no queda ningun temporizador colgando ni se
      // toca el estado de un componente ya desmontado.
      cajaVivo.current = false;
      if (cajaTimer.current) clearTimeout(cajaTimer.current);
      cajaTimer.current = null;
      cajaPendientes.clear();
    };
  }, []);

  return useCallback(
    (id: string) => {
      if (!id) return;
      pendientes.current.add(id);
      // Ya hay una tanda en camino: este id se suma a ella y no abre otra.
      if (timer.current) return;
      timer.current = setTimeout(async () => {
        timer.current = null;
        if (!vivo.current) return;
        const todos = [...pendientes.current];
        pendientes.current.clear();
        if (todos.length === 0) return;
        // Si la rafaga fue enorme, se parte en tandas en vez de armar una
        // direccion kilometrica que el servidor rechaza.
        for (let i = 0; i < todos.length; i += TOPE_POR_TANDA) {
          const tanda = todos.slice(i, i + TOPE_POR_TANDA);
          try {
            const filas = await traerRef.current(tanda);
            if (!vivo.current) return;
            aplicarRef.current(filas ?? [], tanda);
          } catch {
            // Una tanda que falla no rompe la pantalla: el refresco periodico
            // de cada pantalla la pone al dia igual.
          }
        }
      }, respiroMs);
    },
    [respiroMs],
  );
}
