const SERVICE_WORKER_URL = '/termi-web-push-sw.js';

export type BrowserPushSubscriptionPayload = {
  endpoint: string;
  expirationTime?: number | null;
  keys: {
    auth: string;
    p256dh: string;
  };
};

function ensureBrowserSupport() {
  if (
    typeof window === 'undefined' ||
    !('Notification' in window) ||
    !('serviceWorker' in navigator) ||
    !('PushManager' in window)
  ) {
    throw new Error('当前浏览器不支持 Web Push。');
  }
}

function urlBase64ToUint8Array(base64String: string) {
  const normalized = base64String.trim();
  const padding = '='.repeat((4 - (normalized.length % 4)) % 4);
  const base64 = (normalized + padding).replace(/-/g, '+').replace(/_/g, '/');
  const binary = window.atob(base64);
  const output = new Uint8Array(binary.length);

  for (let index = 0; index < binary.length; index += 1) {
    output[index] = binary.charCodeAt(index);
  }

  return output;
}

async function getRegistration() {
  ensureBrowserSupport();
  await navigator.serviceWorker.register(SERVICE_WORKER_URL, { scope: '/' });
  return navigator.serviceWorker.ready;
}

function normalizeSubscription(
  subscription: PushSubscription | PushSubscriptionJSON,
): BrowserPushSubscriptionPayload {
  const json = 'toJSON' in subscription ? subscription.toJSON() : subscription;
  const endpoint = json.endpoint?.trim() || '';
  const auth = json.keys?.auth?.trim() || '';
  const p256dh = json.keys?.p256dh?.trim() || '';

  if (!endpoint || !auth || !p256dh) {
    throw new Error('浏览器返回的 PushSubscription 不完整。');
  }

  return {
    endpoint,
    expirationTime: json.expirationTime ?? null,
    keys: {
      auth,
      p256dh,
    },
  };
}

export function supportsBrowserPush() {
  return (
    typeof window !== 'undefined' &&
    'Notification' in window &&
    'serviceWorker' in navigator &&
    'PushManager' in window
  );
}

export async function getBrowserPushSubscription() {
  if (!supportsBrowserPush()) {
    return null;
  }

  const registration = await getRegistration();
  return registration.pushManager.getSubscription();
}

export async function ensureBrowserPushSubscription(
  publicKey: string,
): Promise<BrowserPushSubscriptionPayload> {
  ensureBrowserSupport();

  if (!publicKey.trim()) {
    throw new Error('Web Push 公钥未配置。');
  }

  const permission =
    Notification.permission === 'granted'
      ? 'granted'
      : await Notification.requestPermission();

  if (permission !== 'granted') {
    throw new Error('浏览器通知权限未开启。');
  }

  const registration = await getRegistration();
  let subscription = await registration.pushManager.getSubscription();

  if (!subscription) {
    subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(publicKey),
    });
  }

  return normalizeSubscription(subscription);
}
