// Vanday — current-user / plan-limits hook.
//
// Single source of truth for "who am I, what plan am I on, what can I do."
// Other components read this via the global useVandayMe() hook so they only
// need to know about React state — no manual fetching, no prop-drilling.

(function () {
  // Cache the response on window so components mounted later don't re-fetch.
  let cache = null;
  let inflight = null;
  const subscribers = new Set();

  function setCache(data) {
    cache = data;
    subscribers.forEach((fn) => { try { fn(data); } catch {} });
  }

  async function fetchMe({ force } = {}) {
    // If a fetch is already in flight, wait for it instead of stacking
    // another. force:true bypasses this so a caller can re-read fresh
    // data after a known mutation (e.g. a successful upload that
    // increments the org's upload count).
    if (inflight && !force) return inflight;
    inflight = (async () => {
      try {
        const r = await fetch("/api/me", {
          headers: await authHeaders(),
        });
        if (!r.ok) { setCache(null); return null; }
        const data = await r.json();
        setCache(data);
        return data;
      } finally { inflight = null; }
    })();
    return inflight;
  }

  async function authHeaders() {
    try {
      const session = window.Clerk && window.Clerk.session;
      if (!session) return {};
      const token = await session.getToken();
      return token ? { Authorization: `Bearer ${token}` } : {};
    } catch { return {}; }
  }

  // Mark the welcome modal as seen on the server, then clear it locally.
  async function markOnboarded() {
    try {
      await fetch("/api/onboarded", {
        method: "POST",
        headers: await authHeaders(),
      });
    } catch {}
    if (cache && cache.user) {
      setCache({ ...cache, user: { ...cache.user, onboardedAt: Date.now() } });
    }
  }

  // React hook: returns the current "me" object, refreshing on demand.
  function useVandayMe() {
    const [me, setMe] = React.useState(cache);
    React.useEffect(() => {
      const handler = (d) => setMe(d);
      subscribers.add(handler);
      if (cache === null && !inflight) { fetchMe(); }
      return () => { subscribers.delete(handler); };
    }, []);
    return me;
  }

  // Convenience: limits with safe defaults so components don't need to check
  // for nulls when /api/me hasn't loaded yet.
  function useVandayLimits() {
    const me = useVandayMe();
    return {
      maxUploads:        me?.limits?.maxUploads ?? "unlimited",
      maxSeats:          me?.limits?.maxSeats ?? 3,
      maxStorageBytes:   me?.limits?.maxStorageBytes ?? (5 * 1024 * 1024 * 1024),
      uploadsUsed:       me?.usage?.uploads ?? 0,
      storageBytesUsed:  me?.usage?.storageBytes ?? 0,
      // Sharing is part of every plan now — default ON when the field is
      // missing/stale. An explicit `false` (the global kill switch) still wins.
      sharingEnabled:    me?.limits?.sharingEnabled ?? true,
      publishingEnabled: me?.limits?.publishingEnabled ?? false,
      // New plan-driven feature flags. UI uses these to decide whether to
      // SHOW a CTA at all (versus showing it and letting the server 403).
      // Defaults true keep beta-grandfathered behavior when /api/me hasn't
      // loaded yet (we'd rather flash a button briefly than hide a paid-for
      // feature from someone who's entitled to it).
      socialPublishing:  me?.limits?.socialPublishing ?? true,
      aiAutoTagging:     me?.limits?.aiAutoTagging ?? true,
      companyDomainAccess: me?.limits?.companyDomainAccess ?? false,
      maxWorkspaces:     me?.limits?.maxWorkspaces ?? 1,
      planName:          me?.plan?.name || "free_beta",
      loaded:            me !== null && me !== undefined,
    };
  }

  // Feature-gate hook: returns the per-surface visibility map driven by
  // VANDAY_MODE. Default everything OFF until /api/me loads so we don't
  // briefly flash beta-restricted UI on first render. Dev mode flips them
  // all on; beta keeps them off.
  function useVandayFeatures() {
    const me = useVandayMe();
    const f = me?.features || {};
    // Per-member capabilities, mirroring the server's role enforcement:
    //   admin / editor -> write + publish, contributor -> write only,
    //   viewer -> read/download only. We only RESTRICT when the role is
    //   positively known to be limited; legacy / personal / super-admin
    //   contexts (no org role, or role "admin") keep full access. This matches
    //   the server, which blocks ONLY a positively-resolved viewer/contributor.
    const orgRole = me?.org?.role || null;
    return {
      mode:             me?.mode || "beta",
      isAdmin:          me?.isAdmin === true,
      // Workspace (org) admin: the role that can manage members + workspace
      // settings. Super-admins (isAdmin) always count. Personal workspaces make
      // their sole member an admin, so solo users keep full access.
      isWorkspaceAdmin: me?.isAdmin === true || me?.org?.role === "admin",
      // canWrite: may upload / edit / delete / organize. Only viewers can't.
      canWrite:         orgRole !== "viewer",
      // canPublish: may push to social. Viewers and contributors can't.
      canPublish:       orgRole !== "viewer" && orgRole !== "contributor",
      sharing:          f.sharing === true,
      publishing:       f.publishing === true,
      apiKeys:          f.apiKeys === true,
      webhooks:         f.webhooks === true,
      audit:            f.audit === true,
      versions:         f.versions === true,
      comments:         f.comments === true,
      contentSecurity:  f.contentSecurity === true,
      usageBilling:     f.usageBilling === true,
      branding:         f.branding === true,
      projects:         f.projects === true,
      notifications:    f.notifications === true,
      integrations:     f.integrations === true,
      // IPTC metadata editor (Info tab in preview, read/write endpoints).
      // Gated per-workspace via the admin dashboard "Features" column.
      // Defaults to false until /api/me confirms — same pattern as the
      // other flags above.
      iptcMetadata:     f.iptcMetadata === true,
      loaded:           me !== null && me !== undefined,
    };
  }

  // Synchronous snapshot of the limits — for non-React code paths that
  // need to read the current count cap (e.g. the upload pre-flight in
  // app.jsx's queueFiles). Reads the same cached `me` the hook uses;
  // returns null when /api/me hasn't loaded yet so callers can fall
  // back to letting the server enforce.
  function useVandayLimitsSnapshot() {
    if (!cache) return null;
    return {
      maxUploads:        cache?.limits?.maxUploads ?? "unlimited",
      maxSeats:          cache?.limits?.maxSeats ?? 3,
      maxStorageBytes:   cache?.limits?.maxStorageBytes ?? (5 * 1024 * 1024 * 1024),
      uploadsUsed:       cache?.usage?.uploads ?? 0,
      storageBytesUsed:  cache?.usage?.storageBytes ?? 0,
      sharingEnabled:    cache?.limits?.sharingEnabled ?? true,
      publishingEnabled: cache?.limits?.publishingEnabled ?? false,
      socialPublishing:  cache?.limits?.socialPublishing ?? true,
      aiAutoTagging:     cache?.limits?.aiAutoTagging ?? true,
      companyDomainAccess: cache?.limits?.companyDomainAccess ?? false,
      maxWorkspaces:     cache?.limits?.maxWorkspaces ?? 1,
      planName:          cache?.plan?.name || "free_beta",
      loaded:            true,
    };
  }

  // Synchronous read of whether billing is rolled out for this workspace — for
  // non-React code paths (e.g. the paywall trigger) that need to decide WITHOUT
  // a hook. Reads the same cached /api/me the hooks use; false until it loads.
  function usageBillingEnabled() {
    return cache?.features?.usageBilling === true;
  }

  window.VandayMe = { fetchMe, markOnboarded, usageBillingEnabled };
  window.useVandayMe = useVandayMe;
  window.useVandayLimits = useVandayLimits;
  window.useVandayLimitsSnapshot = useVandayLimitsSnapshot;
  window.useVandayFeatures = useVandayFeatures;
})();
