// services/analytics/src/render-limiter.ts — sémaphore GLOBAL de rendus
// headless (revue T2 du plan S7). Les rendus Chrome (`src/lib/semantic/
// render.ts`) sont coûteux (spawn + navigation) et tournent DANS le pool de
// fetch (concurrence 4) du runner — mais plusieurs jobs `serp-semantics`
// peuvent aussi tourner en parallèle dans le même process. Ce module borne à
// `MAX_CONCURRENT_RENDERS` le nombre de rendus Chrome simultanés, TOUS JOBS
// CONFONDUS, en enveloppant le `RenderProvider` injecté au runner.
//
// Le limiteur est un état de MODULE (compteur + file FIFO de promesses en
// attente), volontairement partagé par tous les appels du process — c'est le
// but : un cap global, pas un cap par job. `limitRenderConcurrency` est pure
// (aucune dépendance DB/réseau) : testable avec un provider fake.
import type { RenderProvider } from "@/lib/semantic/render";

/** Au plus N Chrome headless simultanés dans tout le process, quel que soit
 *  le nombre de jobs `serp-semantics` en cours (spawn Chrome = coûteux en
 *  CPU/RAM — cf. `render.ts`). */
export const MAX_CONCURRENT_RENDERS = 2;

/**
 * Enveloppe `provider` pour qu'au plus `max` appels soient en vol
 * simultanément. Les appels excédentaires sont mis en file FIFO et démarrent
 * dès qu'un slot se libère (succès OU échec du rendu — libération en
 * `finally`, jamais de fuite de slot).
 */
export function limitRenderConcurrency(
  provider: RenderProvider,
  max: number = MAX_CONCURRENT_RENDERS,
): RenderProvider {
  let active = 0;
  const queue: Array<() => void> = [];

  const acquire = (): Promise<void> => {
    if (active < max) {
      active++;
      return Promise.resolve();
    }
    return new Promise((resolve) => {
      queue.push(() => {
        active++;
        resolve();
      });
    });
  };

  const release = (): void => {
    active--;
    const next = queue.shift();
    if (next) next();
  };

  return async (url: string) => {
    await acquire();
    try {
      return await provider(url);
    } finally {
      release();
    }
  };
}
