// Décision d'un tick de polling, isolée du composant pour être testable sans navigateur.
//
// Deux échecs ne se valent pas, et c'est tout l'objet de ce module : une **réponse de refus**
// de l'api (404 : ce job n'existe pas) ne s'améliorera jamais, insister est du bruit ; un
// **appel qui n'aboutit pas** (wifi qui hoquette, dev server redémarré, proxy) est transitoire
// et c'est exactement le cas où abandonner laisse l'utilisateur devant une barre « En cours »
// perpétuelle alors que son job avance très bien côté serveur.

/** Nombre d'échecs de transport **consécutifs** tolérés avant d'abandonner le suivi. */
export const POLL_MAX_FAILURES = 3;

export type PollOutcome<T> =
  /** L'api a répondu : voilà l'état du job. */
  | { status: "value"; value: T }
  /** L'api a répondu un refus (404, 422…) : inutile de réessayer. */
  | { status: "refused"; error: string }
  /** L'appel lui-même a échoué : on retente, jusqu'à `POLL_MAX_FAILURES`. */
  | { status: "unreachable"; error: string };

export type PollStep =
  /** État terminal atteint : plus rien à interroger. */
  | { action: "stop" }
  /** Interroger de nouveau après l'intervalle. */
  | { action: "continue" }
  /** Abandonner et montrer l'erreur. */
  | { action: "fail"; error: string };

/**
 * Que faire après un tick. `failures` compte les échecs de transport consécutifs **y compris
 * celui-ci** ; il est remis à zéro par l'appelant dès qu'une réponse arrive.
 */
export function nextPollStep<T>(
  outcome: PollOutcome<T>,
  isFinal: (value: T) => boolean,
  failures: number,
  maxFailures: number = POLL_MAX_FAILURES,
): PollStep {
  if (outcome.status === "value") {
    return isFinal(outcome.value) ? { action: "stop" } : { action: "continue" };
  }
  if (outcome.status === "refused") return { action: "fail", error: outcome.error };
  return failures >= maxFailures
    ? { action: "fail", error: outcome.error }
    : { action: "continue" };
}
