// Bridge the live Matrix session (baseUrl + accessToken) to the service
// worker, which attaches `Authorization: Bearer <token>` to authenticated
// media requests (/_matrix/client/v1/media/{download,thumbnail}) — see
// src/sw.ts fetch handler. Without this, media GETs hit the homeserver
// unauthenticated and 401.
//
// First-load race: on the very first visit after SW install,
// `navigator.serviceWorker.controller` is null — the worker has registered
// but not yet taken over (it calls `self.clients.claim()` in its activate
// handler). A direct postMessage at that moment silently drops, and there is
// no controller to receive it. So we keep the latest session in
// `pendingSession` and re-attempt when (a) the registration becomes ready and
// (b) on every `controllerchange`. This mirrors the language bridge in
// src/app/utils/pushLanguageBridge.ts, which already covers this exact window.
// Both retry paths no-op once the controller is set and the post has landed,
// so duplicates are cheap.

type PendingSession = { baseUrl?: string; accessToken?: string };

let pendingSession: PendingSession | undefined;
let swControllerWatchInstalled = false;

const trySendSession = (): void => {
  if (pendingSession === undefined) return;
  if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) return;
  const sw = navigator.serviceWorker;
  if (!sw.controller) return;
  try {
    sw.controller.postMessage({
      type: 'setSession',
      accessToken: pendingSession.accessToken,
      baseUrl: pendingSession.baseUrl,
    });
  } catch {
    /* ignore — next controllerchange re-posts */
  }
};

const installSwControllerWatch = (): void => {
  if (swControllerWatchInstalled) return;
  if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) return;
  swControllerWatchInstalled = true;
  const sw = navigator.serviceWorker;
  // `ready` resolves once there is an active registration; our SW then calls
  // `self.clients.claim()` in its activate handler, so shortly after `ready`
  // the page gains a controller and `controllerchange` fires. We listen to
  // both so the first-install window is covered regardless of which fires
  // first.
  sw.ready.then(trySendSession).catch(() => undefined);
  sw.addEventListener('controllerchange', trySendSession);
};

export function pushSessionToSW(baseUrl?: string, accessToken?: string) {
  if (!('serviceWorker' in navigator)) return;
  // Remember the latest session even when there is no controller yet, then
  // attempt delivery now and again whenever the controller appears. Logout
  // passes both args undefined, which the SW reads as "clear session".
  pendingSession = { baseUrl, accessToken };
  installSwControllerWatch();
  trySendSession();
}
