// src/lib/tours/should-start.ts — la seule règle qui décide si un tour démarre.
// Pure et sans dépendance runtime : `TourStatus` est importé en `import type`,
// donc l'import de ./progress (qui est "server-only") est effacé à la
// compilation et ce module reste utilisable côté client.
import type { TourStatus } from "./progress";

export type ShouldStartInput = {
  /** Statut enregistré pour ce tour, `undefined` si jamais vu. */
  status: TourStatus | undefined;
  /**
   * La page a-t-elle quelque chose à montrer ?
   * C'est la règle qui gouverne tout le système : on n'explique pas le
   * Planning en pointant un tableau à zéro ligne.
   */
  hasContent: boolean;
};

/**
 * Verdict NOMMÉ, et non un booléen.
 *
 * Cette fonction a d'abord rendu un `bool`. C'était un type trop pauvre pour
 * ce qu'elle décide, à deux titres :
 *
 * 1. `false` recouvrait deux situations opposées — « reporte, n'enregistre
 *    rien » (l'écran se remplira) et « refuse, le tour a déjà eu sa chance ».
 *    Un appelant qui enregistrait « rejeté » sur la mauvaise branche faisait
 *    perdre définitivement son tour à tout utilisateur au tableau vide.
 * 2. Une conjonction pure de booléens rendant un booléen a un ordre
 *    d'évaluation SÉMANTIQUEMENT INOBSERVABLE : intervertir les lignes
 *    ci-dessous ne changeait aucun résultat, donc aucun test n'aurait pu
 *    asservir la priorité de la précondition de contenu. Ce n'était pas un
 *    test manquant, c'était le type. Avec un verdict distinct par branche,
 *    l'ordre devient une observation : (statut enregistré + écran vide) doit
 *    rendre `defer-empty`, jamais `already-seen`.
 *
 * Le préfixe `defer-` dit la nature du refus :
 * - `defer-*` : REPORT. Rien à enregistrer, le tour sera reproposé.
 * - les autres : le tour n'a pas lieu d'être ici et maintenant.
 *
 * AUCUN verdict de cette fonction n'autorise une écriture, `eligible` compris :
 * le statut ne s'écrit que sur une fin ou un rejet explicites de l'utilisateur.
 *
 * « Un autre tour est-il déjà à l'écran ? » NE SE POSE PAS ICI, et c'est
 * délibéré. Cette règle ne voit ni le DOM ni l'instance driver.js ; seul
 * `planTour` en a le moyen, et il tranche la question AVANT d'appeler cette
 * fonction — un tour en cours l'emporte sur tout, y compris sur une relance
 * explicite. Tant que la question vivait aux deux endroits, elle recevait deux
 * réponses de précédence OPPOSÉE (ici : écran vide d'abord ; là-bas : tour en
 * cours d'abord), et la branche d'ici était inatteignable en production
 * puisque `planTour` lui passait un `false` en dur. Deux tests affirmaient donc
 * une priorité que le système réel inversait.
 */
export type StartRuleVerdict =
  /** Rien ne s'oppose au démarrage. */
  | { kind: "eligible" }
  /** REPORT — la page ne se déclare pas non vide. */
  | { kind: "defer-empty" }
  /** Terminé ou rejeté lors d'une visite précédente. */
  | { kind: "already-seen" };

export function startRuleVerdict(input: ShouldStartInput): StartRuleVerdict {
  // Précondition de contenu d'abord, et elle ne souffre aucune exception :
  // même un tour jamais vu ne démarre pas sur un écran vide. L'ordre de ces
  // deux lignes est asservi par should-start.test.ts, cas par cas.
  if (!input.hasContent) return { kind: "defer-empty" };
  if (input.status !== undefined) return { kind: "already-seen" };
  return { kind: "eligible" };
}
