/**
 * Push notification subscription (Session 27).
 *
 * Client-side helpers to request permission and (un)subscribe to Web Push
 * via the active service worker. The SW already handles incoming `push`
 * and `notificationclick` events (see web/src/sw.ts); these helpers manage
 * the browser→push-service subscription handshake.
 *
 * VAPID: subscription requires NEXT_PUBLIC_VAPID_PUBLIC_KEY. Until that
 * key is generated and set (a future session), subscribeToPush returns
 * null rather than throwing — callers degrade gracefully.
 *
 * The resulting PushSubscription is meant to be POSTed to the backend and
 * stored (Supabase) so the notification trigger system can target it.
 */

/** Browser support check — both SW and PushManager must exist. */
export function isPushSupported(): boolean {
  return (
    typeof window !== 'undefined' &&
    'serviceWorker' in navigator &&
    'PushManager' in window &&
    'Notification' in window
  );
}

/**
 * VAPID public keys are base64url; the subscribe API wants a BufferSource.
 * We return the backing ArrayBuffer so the type is unambiguously
 * ArrayBuffer (not ArrayBufferLike) for `applicationServerKey`.
 */
function vapidKeyToBuffer(base64String: string): ArrayBuffer {
  const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
  const raw = window.atob(base64);
  const buffer = new ArrayBuffer(raw.length);
  const view = new Uint8Array(buffer);
  for (let i = 0; i < raw.length; i += 1) view[i] = raw.charCodeAt(i);
  return buffer;
}

/**
 * Request permission and subscribe. Returns the PushSubscription on
 * success, or null when unsupported / denied / VAPID key missing.
 */
export async function subscribeToPush(): Promise<PushSubscription | null> {
  if (!isPushSupported()) return null;

  const vapidKey = process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY;
  if (!vapidKey) {
    console.warn('[push] NEXT_PUBLIC_VAPID_PUBLIC_KEY not set — cannot subscribe yet.');
    return null;
  }

  const permission = await Notification.requestPermission();
  if (permission !== 'granted') return null;

  const registration = await navigator.serviceWorker.ready;

  // Reuse an existing subscription if the browser already has one.
  const existing = await registration.pushManager.getSubscription();
  if (existing) return existing;

  return registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: vapidKeyToBuffer(vapidKey),
  });
}

/** Unsubscribe the active push subscription, if any. Idempotent. */
export async function unsubscribeFromPush(): Promise<boolean> {
  if (!isPushSupported()) return true;
  const registration = await navigator.serviceWorker.ready;
  const subscription = await registration.pushManager.getSubscription();
  if (!subscription) return true;
  return subscription.unsubscribe();
}

/** Current permission state without prompting — for UI affordances. */
export function pushPermission(): NotificationPermission | 'unsupported' {
  if (!isPushSupported()) return 'unsupported';
  return Notification.permission;
}
