/*!
 * WRNexus Web Push — service worker.
 *
 * ┌─────────────────────────────────────────────────────────────────────────┐
 * │ HOST THIS FILE ON YOUR OWN ORIGIN.                                        │
 * │                                                                          │
 * │ A service worker can only control the origin it is served from, so the   │
 * │ browser will NOT let wrnexus.com register a worker for your site. Copy    │
 * │ this file to the ROOT of your site and serve it at:                       │
 * │                                                                          │
 * │     https://your-domain.example/wrnexus-push-sw.js                        │
 * │                                                                          │
 * │ It must be served same-origin, over HTTPS, with                          │
 * │ `Content-Type: text/javascript`. To control the whole site from a        │
 * │ non-root path, also send `Service-Worker-Allowed: /`.                     │
 * └─────────────────────────────────────────────────────────────────────────┘
 *
 * Core behaviour needs NO configuration — the click/delivery tracking URLs are
 * carried inside each push payload built by the WRNexus API. The optional
 * CONFIG block below is only used to silently re-subscribe after the browser
 * rotates a subscription (`pushsubscriptionchange`); leave it blank to skip
 * that.
 */

/* eslint-disable no-restricted-globals */

// ── OPTIONAL: only needed for automatic re-subscription on key rotation ──────
var WRNEXUS_PUSH_CONFIG = {
  // Your site's publicKey (the same value as data-site-id in the snippet).
  siteKey: '',
  // The WRNexus API origin (e.g. 'https://api.wrnexus.com').
  apiBase: '',
  // Your site's VAPID public key (shown in Push → Settings).
  vapidPublicKey: '',
};

self.addEventListener('install', function () {
  self.skipWaiting();
});

self.addEventListener('activate', function (event) {
  event.waitUntil(self.clients.claim());
});

// CORS-simple POST (text/plain, no custom headers, no credentials) so the
// receipt reaches the WRNexus API cross-origin without a preflight.
function postReceipt(url) {
  if (!url) return Promise.resolve();
  return fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'text/plain;charset=UTF-8' },
    credentials: 'omit',
    keepalive: true,
    mode: 'cors',
  }).catch(function () {});
}

function urlBase64ToUint8Array(base64String) {
  var padding = '='.repeat((4 - (base64String.length % 4)) % 4);
  var base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
  var raw = self.atob(base64);
  var out = new Uint8Array(raw.length);
  for (var i = 0; i < raw.length; ++i) out[i] = raw.charCodeAt(i);
  return out;
}

function keyToB64(buf) {
  if (!buf) return '';
  var bytes = new Uint8Array(buf);
  var binary = '';
  for (var i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
  return self
    .btoa(binary)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
}

// ── Incoming notification ─────────────────────────────────────────────────
self.addEventListener('push', function (event) {
  var data = {};
  try {
    data = event.data ? event.data.json() : {};
  } catch (_e) {
    try {
      data = { title: event.data && event.data.text() };
    } catch (_e2) {
      data = {};
    }
  }

  var title = data.title || 'Notification';
  var options = {
    body: data.body || '',
    icon: data.icon || undefined,
    image: data.image || undefined,
    badge: data.badge || undefined,
    tag: data.tag || data.campaignId || undefined,
    requireInteraction: !!data.requireInteraction,
    data: {
      clickUrl: data.clickUrl || data.url || '/',
      url: data.url || '/',
      openUrl: data.openUrl || '',
      campaignId: data.campaignId || '',
      subscriptionId: data.subscriptionId || '',
    },
  };

  event.waitUntil(
    Promise.all([
      self.registration.showNotification(title, options),
      // Delivery receipt — records a `delivered` event for analytics.
      postReceipt(data.deliveredUrl),
    ])
  );
});

// ── Click → record + open the destination ─────────────────────────────────
self.addEventListener('notificationclick', function (event) {
  var d = (event.notification && event.notification.data) || {};
  event.notification.close();

  // `clickUrl` is a signed WRNexus redirect: hitting it records the `clicked`
  // event and 302s to the real destination, so tracking fires from the open.
  var target = d.clickUrl || d.url || '/';

  event.waitUntil(
    self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (list) {
      // Reuse an existing tab on the destination when possible.
      for (var i = 0; i < list.length; i++) {
        var client = list[i];
        if (d.url && client.url === d.url && 'focus' in client) return client.focus();
      }
      if (self.clients.openWindow) return self.clients.openWindow(target);
      return undefined;
    })
  );
});

// ── Optional: re-subscribe after the browser rotates the subscription ──────
self.addEventListener('pushsubscriptionchange', function (event) {
  var cfg = WRNEXUS_PUSH_CONFIG || {};
  if (!cfg.siteKey || !cfg.apiBase || !cfg.vapidPublicKey) return;
  event.waitUntil(
    self.registration.pushManager
      .subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(cfg.vapidPublicKey) })
      .then(function (sub) {
        return fetch(cfg.apiBase.replace(/\/+$/, '') + '/api/push/subscriptions', {
          method: 'POST',
          headers: { 'Content-Type': 'text/plain;charset=UTF-8' },
          credentials: 'omit',
          mode: 'cors',
          body: JSON.stringify({
            key: cfg.siteKey,
            endpoint: sub.endpoint,
            keys: {
              p256dh: keyToB64(sub.getKey('p256dh')),
              auth: keyToB64(sub.getKey('auth')),
            },
            metadata: { source: 'pushsubscriptionchange' },
          }),
        }).catch(function () {});
      })
      .catch(function () {})
  );
});
