// src/lib/mangools/throttle.ts — throttle pur (aucune dépendance runtime, PAS
// de `server-only`). Sérialise les exécutions en file (promise-chain) et espace
// chaque démarrage d'au moins `minIntervalMs` (défaut 500 ms ⇒ ~2 req/s).
// Horloge et sleep sont injectables pour des tests instantanés.

export type ThrottleOptions = {
  /** Intervalle minimal entre deux démarrages de run. Défaut 500 ms. */
  minIntervalMs?: number;
  /** Source de temps (ms). Défaut `Date.now`. Injectable pour les tests. */
  now?: () => number;
  /** Fonction d'attente. Défaut `setTimeout`. Injectable pour les tests. */
  sleep?: (ms: number) => Promise<void>;
};

export type Throttle = {
  /** Exécute `fn` en file, en garantissant l'espacement et la sérialisation. */
  run<T>(fn: () => Promise<T>): Promise<T>;
  /** Le sleep effectif (injecté ou par défaut) — réutilisé par le client pour
   *  ses backoffs 429, ce qui les rend instantanés en test sans nouveau param. */
  sleep(ms: number): Promise<void>;
};

const defaultSleep = (ms: number): Promise<void> =>
  new Promise((resolve) => setTimeout(resolve, ms));

export function createThrottle(opts: ThrottleOptions = {}): Throttle {
  const minIntervalMs = opts.minIntervalMs ?? 500;
  const now = opts.now ?? Date.now;
  const sleep = opts.sleep ?? defaultSleep;

  let chain: Promise<unknown> = Promise.resolve();
  let lastStart = Number.NEGATIVE_INFINITY;

  function run<T>(fn: () => Promise<T>): Promise<T> {
    const result = chain.then(async () => {
      const wait = minIntervalMs - (now() - lastStart);
      if (wait > 0) await sleep(wait);
      lastStart = now();
      return fn();
    });
    // La file continue quel que soit le sort de `result` (succès ou rejet).
    chain = result.then(
      () => undefined,
      () => undefined,
    );
    return result;
  }

  return { run, sleep };
}

// Registre module-level : un throttle par organisation (partagé entre requêtes).
const registry = new Map<string, Throttle>();

export function getOrgThrottle(orgId: string): Throttle {
  let existing = registry.get(orgId);
  if (!existing) {
    existing = createThrottle();
    registry.set(orgId, existing);
  }
  return existing;
}
