// src/lib/auth/impersonation-blocked-paths.ts — liste pure, sans import.
//
// Module volontairement SANS dépendance : il est consommé par `hooks.before`
// dans src/lib/auth.ts, donc il ne peut ni importer `auth` (cycle) ni
// `server-only` (impersonation-guard.ts fait les deux, d'où ce module séparé).
// Donnée pure ⇒ testable nue, sans session ni serveur.

/**
 * Endpoints Better Auth interdits pendant une impersonation.
 *
 * Ces routes sont appelées directement par `authClient.*` et n'entrent jamais
 * dans nos Server Actions : la seule interception possible est `hooks.before`.
 * Pendant une impersonation la session vivante est celle de la personne cible,
 * donc chacune de ces écritures modifierait SON compte — et sans laisser de
 * trace, puisque les gardes SYS échouent dans ce contexte
 * (`isCurrentUserSuperAdmin()` lit l'email de la cible) et que
 * `recordAdminAction` n'est donc jamais atteint.
 *
 * Correspondance EXACTE, jamais par préfixe : `/admin/stop-impersonating` doit
 * rester joignable, sinon l'opérateur est enfermé dans la session de la cible —
 * un incident bien pire que le problème corrigé. Aucun chemin sous `/admin/`
 * n'a sa place ici (le plugin admin est la surface légitime de l'opérateur, et
 * elle passe déjà par nos gardes SYS + `recordAdminAction`).
 *
 * Chemins vérifiés un par un dans better-auth 1.6.23 :
 *   `dist/api/routes/*.mjs` (cœur) et `dist/plugins/two-factor/index.mjs`.
 *   `/set-password` du brief a été RETIRÉ : dans cette version c'est un
 *   `createAuthEndpoint.serverOnly()` sans path (update-user.mjs), donc aucune
 *   route HTTP — notre seul appelant est `setInitialPasswordAction`, déjà gardé
 *   côté Server Action.
 *
 * HORS PÉRIMÈTRE — cette liste couvre les écritures de COMPTE, et RIEN D'AUTRE.
 * Ne pas la lire comme la surface fermée de l'impersonation. Les écritures
 * d'ORGANISATION et de FACTURATION sont atteignables depuis le navigateur du
 * client, clées sur la session impersonnée, et ne sont couvertes par AUCUN des
 * deux mécanismes (ni cette liste, ni `sessionIsImpersonated` dans les Server
 * Actions) :
 *   - facturation : `/subscription/cancel`, `/subscription/upgrade`,
 *     `/subscription/restore` — un opérateur impersonnant un propriétaire peut
 *     résilier l'abonnement du client, sans trace ;
 *   - organisation : `/organization/leave`, `/organization/delete`,
 *     `/organization/remove-member`, `/organization/update-member-role`,
 *     et les autres écritures du plugin organization.
 * Ces familles sont PRÉ-EXISTANTES et hors périmètre de la présente correction :
 * les bloquer suppose d'abord de trancher une question produit — un opérateur
 * a-t-il des raisons légitimes d'agir sur l'organisation d'un client pendant une
 * impersonation (support facturation, retrait d'un membre à sa demande) ? Tant
 * que cette décision n'est pas prise, n'ajouter aucun de ces chemins ici.
 */
export const IMPERSONATION_BLOCKED_PATHS: ReadonlySet<string> = new Set([
  // Cœur — écritures sur le compte / les sessions.
  "/update-user",
  "/change-email",
  "/change-password",
  "/delete-user",
  // Le plus grave : rattacherait le compte Google de l'opérateur à la cible,
  // où il deviendrait candidat au jeton Search Console de tout site dont la
  // cible est l'autorité (`sites.gsc_authorized_user_id`).
  "/link-social",
  "/unlink-account",
  "/revoke-session",
  "/revoke-sessions",
  "/revoke-other-sessions",
  // Jetons OAuth du fournisseur. `resolveUserId` (account.mjs) privilégie la
  // SESSION sur le `userId` du corps (`session?.user?.id || userId`) : pendant
  // une impersonation, un simple fetch depuis le navigateur de l'opérateur
  // renvoie donc le jeton Google VIVANT de la cible — exfiltrable, réutilisable
  // hors de l'app (Search Console, Drive selon les scopes), et sans trace.
  // Bloquer ne casse PAS la synchro Search Console : les deux appelants vivants
  // (`src/lib/gsc/site-token.ts`, qui résout le jeton du couple
  // (utilisateur, compte Google) épinglé sur le site, et `src/lib/gsc/actions.ts`,
  // qui énumère les propriétés des comptes de l'utilisateur courant) invoquent
  // `auth.api.getAccessToken({ body: { providerId, userId, accountId } })` SANS
  // `headers`, donc `getSessionFromCtx` y renvoie `null` et cette garde ne se
  // déclenche jamais sur eux.
  "/get-access-token",
  "/refresh-token",
  // Plugin twoFactor — ces quatre routes réussissent aujourd'hui avec le mot de
  // passe de la CIBLE uniquement, ce qui est un garde-fou réel mais insuffisant :
  //  1. Trou d'audit — c'est le motif principal. Une bascule de 2FA pendant une
  //     impersonation ne laisse AUCUNE trace : `isCurrentUserSuperAdmin()` lit
  //     l'email de la cible, donc `recordAdminAction` n'est jamais atteint.
  //     Une désactivation de 2FA imputée à la cible elle-même est indétectable.
  //  2. Robustesse — la garde mot de passe tient à `allowPasswordless`, que ce
  //     projet ne configure pas : `shouldRequirePassword` court-circuite dessus
  //     (`utils/password.mjs`, `if (!allowPasswordless) return true`). L'activer
  //     un jour, ou naître d'un magic link / de Google, ferait tomber le seul
  //     rempart — sans que personne ne pense à revenir ici.
  // `get-totp-uri` est une lecture, mais elle divulgue le secret TOTP partagé
  // de façon permanente : même classe de dommage, même absence de trace.
  "/two-factor/enable",
  "/two-factor/disable",
  "/two-factor/generate-backup-codes",
  "/two-factor/get-totp-uri",
]);

/** Correspondance exacte — jamais `startsWith`. Voir le commentaire ci-dessus. */
export function isBlockedDuringImpersonation(path: string): boolean {
  return IMPERSONATION_BLOCKED_PATHS.has(path);
}
