/// <reference path="../pb_data/types.d.ts" />
//
// Shared helpers for pb_hooks/*.pb.js callbacks.
//
// PocketBase's JSVM serializes and runs each registered hook callback as its
// own isolated program, so a plain top-level function declared in a *.pb.js
// file is NOT in scope inside a callback at call time. Reusable helpers must
// instead live in a plain (non *.pb.js, so it isn't auto-loaded as a hook)
// module and be pulled in per-callback via require(`${__hooks}/utils.js`).
// See: https://github.com/pocketbase/pocketbase/discussions/3599
//
// Loaded modules use a shared registry across callback invocations, so keep
// this file stateless. (Deliberate exception: the warn-once latch below, which
// exists precisely BECAUSE the registry is shared — it dedupes the env-missing
// warning across the bootstrap hook and the per-minute cron tick.)
//
// Keep resolveRole in sync with src/lib/azureAuth.js's resolveRole (the
// canonical, unit-tested version used by the frontend).

let warnedEnvMissing = false;

module.exports = {
  /**
   * Resolve a user's role from their e-mail and the ENTRA_ADMIN_EMAILS allow-list.
   * @param {string} email
   * @returns {"admin"|"user"}
   */
  resolveRole: function (email) {
    const raw = ($os.getenv("ENTRA_ADMIN_EMAILS") || "").toLowerCase();
    const allow = raw.split(",").map(function (s) { return s.trim(); }).filter(Boolean);
    return allow.indexOf(String(email || "").toLowerCase()) !== -1 ? "admin" : "user";
  },

  /**
   * Reconcile the Entra (Azure) OIDC provider on the team_members auth
   * collection from the ENTRA_* environment variables (issue #18).
   *
   * Idempotent: compares the desired provider config against the stored one
   * and only saves when something actually changed, so it is safe to call on
   * every bootstrap and cron tick. Keeping this OUT of the one-shot migration
   * means late or rotated secrets are picked up on the next start/tick without
   * any manual re-apply.
   *
   * @param {core.App} app
   * @returns {"collection-missing"|"not-auth-yet"|"env-missing"|"in-sync"|"updated"}
   */
  reconcileEntraOidc: function (app) {
    let col;
    try {
      col = app.findCollectionByNameOrId("team_members");
    } catch (_) {
      return "collection-missing"; // migration has not created it yet
    }
    if (col.type !== "auth") {
      return "not-auth-yet"; // pre-conversion PIN-era collection
    }

    const tenant       = $os.getenv("ENTRA_TENANT_ID") || "common";
    const clientId     = $os.getenv("ENTRA_CLIENT_ID") || "";
    const clientSecret = $os.getenv("ENTRA_CLIENT_SECRET") || "";
    if (!clientId || !clientSecret) {
      if (!warnedEnvMissing) {
        warnedEnvMissing = true;
        console.log(
          "WARN entra_oidc: ENTRA_CLIENT_ID / ENTRA_CLIENT_SECRET not set — Microsoft login stays " +
          "disabled until the env vars are provided (they are reconciled automatically on startup)."
        );
      }
      return "env-missing";
    }
    warnedEnvMissing = false; // env restored — re-arm the warning for future rotations

    const desired = {
      name:         "oidc",
      clientId:     clientId,
      clientSecret: clientSecret,
      authURL:      "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/authorize",
      tokenURL:     "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/token",
      // Deliberately empty: claims are read from the Entra id_token instead of
      // Graph's /oidc/userinfo. The userinfo endpoint omits `email` for
      // accounts without a mail attribute, while the id_token always carries
      // `preferred_username` (UPN) which entra_oidc.pb.js uses as an e-mail
      // fallback (issue #24).
      userInfoURL:  "",
      displayName:  "Microsoft",
      pkce:         true,
    };

    const providers = (col.oauth2 && col.oauth2.providers) || [];
    const current = providers.length === 1 ? providers[0] : null;
    const inSync = !!current &&
      col.oauth2.enabled === true &&
      current.name         === desired.name &&
      current.clientId     === desired.clientId &&
      current.clientSecret === desired.clientSecret &&
      current.authURL      === desired.authURL &&
      current.tokenURL     === desired.tokenURL &&
      current.userInfoURL  === desired.userInfoURL &&
      current.displayName  === desired.displayName;
    if (inSync) {
      return "in-sync";
    }

    col.oauth2.enabled = true;
    col.oauth2.providers = [desired];
    app.save(col);
    return "updated";
  },
};
