// src/hooks/enrich-polling-logic.ts — logique pure du polling d'enrichissement,
// isolée du hook (qui, lui, importe l'action serveur → graphe DB). Ce module ne
// dépend QUE des types : il est testable sans entraîner le graphe DB ni fausser
// l'ordre de chargement des autres suites (les `import` liés se figent tôt).
import type { KeywordEnrichment } from "@/lib/keywords/types";

/** Intervalle de poll (ms) tant qu'un enrichissement est en vol. */
export const ENRICH_POLL_MS = 2500;

/** Pur, testable : reste-t-il au moins un statut « actif » (`queued|syncing`)
 *  toutes sources confondues ? C'est la condition de continuation du poll. */
export function hasActiveEnrich(
  statuses: Record<string, KeywordEnrichment>,
): boolean {
  for (const enrichment of Object.values(statuses)) {
    for (const state of Object.values(enrichment)) {
      if (state && (state.status === "queued" || state.status === "syncing")) {
        return true;
      }
    }
  }
  return false;
}

/** Pur, testable : un enrichissement vient-il de se TERMINER ? (actif au render
 *  précédent, inactif maintenant). C'est le signal « rafraîchir la table pour
 *  afficher les métriques fraîches » une fois que le spinner s'éteint. La garde
 *  anti-boucle est la comparaison elle-même : tant que `next` reste false, un
 *  `prev` déjà false renvoie false → aucun refresh redéclenché en chaîne. */
export function didEnrichSettle(
  prevActive: boolean,
  nextActive: boolean,
): boolean {
  return prevActive && !nextActive;
}

/** Pur, testable : une réponse de poll est-elle OBSOLÈTE ? Son numéro de
 *  séquence n'est plus le dernier émis (une requête plus récente est partie
 *  entre-temps) → on jette sa réponse pour ne jamais écraser un état plus frais
 *  avec un résultat out-of-order. */
export function isStaleResponse(seq: number, latest: number): boolean {
  return seq !== latest;
}
