// src/components/onboarding/steps/gsc-step-logic.ts — décisions PURES de
// l'étape Search Console du wizard (ce que l'écran propose, ce que fait un
// clic). Séparé du composant pour deux raisons : le module reste chargeable
// sous `--conditions=react-server` (le script `bun run test` du dépôt), et la
// règle du parcours — « l'étape ne bloque JAMAIS » — devient une propriété
// testable plutôt qu'une promesse de JSX.
import type {
  GscStatus,
  OwnedGscProperty,
  SetSiteGscResult,
} from "@/lib/gsc/actions";

/**
 * Ce que l'étape propose, dérivé du seul `GscStatus`. Toutes les branches
 * portent la même sortie (« mon site est trop récent ») : elle est rendue par
 * le composant HORS de ce bloc, pour qu'aucune vue ne puisse l'oublier.
 */
export type GscStepView =
  /** Aucun compte Google : proposer la liaison. */
  | { kind: "link" }
  /** Compte lié mais sans le scope Search Console : proposer la reconnexion. */
  | { kind: "reconnect" }
  /** Compte lié, propriétés lisibles : sélecteur de propriété pour CE site. */
  | {
      kind: "select";
      properties: OwnedGscProperty[];
      /** Propriété déjà mappée sur le site courant, s'il y en a une. */
      current: string | null;
      /** Au moins un compte Google n'a pas répondu : la liste est un plancher,
       *  pas une mesure. On la propose quand même — elle est utilisable. */
      incomplete: boolean;
    }
  /** Google n'a pas répondu ET on n'a rien à proposer. Ce n'est PAS « tu n'as
   *  aucune propriété » : confondre les deux pousse l'utilisateur à croire son
   *  compte vide alors qu'une panne transitoire l'empêche de la lire. */
  | { kind: "unavailable" }
  /** Compte lié, Google a répondu, zéro propriété vérifiée : le cas normal
   *  d'un site qui vient d'être mis en ligne. */
  | { kind: "empty" };

export function resolveGscStepView(
  status: GscStatus,
  siteId: string,
): GscStepView {
  if (status.myGoogle === "none") return { kind: "link" };
  if (status.myGoogle === "missing-scope") return { kind: "reconnect" };
  if (status.properties.length === 0) {
    return status.propertiesError ? { kind: "unavailable" } : { kind: "empty" };
  }
  return {
    kind: "select",
    properties: status.properties,
    current: status.sites.find((s) => s.id === siteId)?.gscProperty ?? null,
    incomplete: status.propertiesError,
  };
}

export type GscStepHandlerDeps = {
  /** `setSiteGscPropertyAction` en production, un espion dans les tests. */
  setProperty: (
    siteId: string,
    property: string | null,
  ) => Promise<SetSiteGscResult>;
  /** Propriété mappée : l'état dérivé côté serveur a changé, l'appelant
   *  recharge (il n'avance PAS localement — le wizard se recompose côté
   *  serveur, l'étape disparaît d'elle-même). */
  onMapped: () => void;
  /** Sortie : on avance sans rien écrire. */
  onSkip: () => void;
  onError: (message: string) => void;
  /** Message quand l'action LÈVE (panne réseau) — le serveur n'en a fourni
   *  aucun. Passé par le composant, seul détenteur des traductions. */
  fallbackError: string;
};

export function createGscStepHandlers(
  siteId: string,
  deps: GscStepHandlerDeps,
): { skip: () => void; select: (property: string) => Promise<void> } {
  return {
    skip() {
      // Aucune écriture : passer l'étape ne mappe rien et ne laisse aucune
      // dette. `connect-gsc` est facultative, elle réapparaîtra simplement
      // dans la checklist d'activation.
      deps.onSkip();
    },
    async select(property: string) {
      try {
        const res = await deps.setProperty(siteId, property);
        // Message du serveur affiché TEL QUEL : il distingue « cette propriété
        // n'est pas à toi » de « Google n'a pas répondu ». Une phrase générique
        // enverrait l'utilisateur démapper sur une panne transitoire.
        if (!res.ok) {
          deps.onError(res.error);
          return;
        }
        deps.onMapped();
      } catch {
        // Le server action peut rejeter (réseau) : sans ce catch, la promesse
        // rejetterait en silence et l'écran resterait figé sur « … ».
        deps.onError(deps.fallbackError);
      }
    },
  };
}
