/**
 * SPD — Service Worker
 * Handles PWA caching + Web Push notifications with vibration & sound
 */

const CACHE  = 'spd-v2';
const STATIC = ['/'];

// ─── Lifecycle ────────────────────────────────────────────────────────────────

self.addEventListener('install', (e) => {
  self.skipWaiting();
  e.waitUntil(
    caches.open(CACHE).then(c => c.addAll(STATIC).catch(() => {}))
  );
});

self.addEventListener('activate', (e) => {
  e.waitUntil(
    Promise.all([
      self.clients.claim(),
      caches.keys().then(keys =>
        Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))
      ),
    ])
  );
});

// ─── Fetch (cache-first for static assets, network-first otherwise) ──────────

self.addEventListener('fetch', (e) => {
  if (e.request.method !== 'GET') return;

  const url = e.request.url;
  // Skip push/API/livewire routes — always network
  if (url.includes('/push/') || url.includes('/api/') || url.includes('/livewire')) return;

  // Cache-first for fonts, images, compiled assets
  if (/\.(woff2?|ttf|eot|svg|png|jpg|webp|ico|css|js)(\?|$)/.test(url)) {
    e.respondWith(
      caches.match(e.request).then(cached => {
        if (cached) return cached;
        return fetch(e.request).then(res => {
          if (res.ok) {
            const clone = res.clone();
            caches.open(CACHE).then(c => c.put(e.request, clone));
          }
          return res;
        }).catch(() => cached);
      })
    );
    return;
  }

  // Network-first for HTML / dynamic pages
  e.respondWith(
    fetch(e.request).catch(() => caches.match(e.request))
  );
});

// ─── Push Notification ────────────────────────────────────────────────────────

self.addEventListener('push', (e) => {
  if (!e.data) return;

  let payload;
  try {
    payload = e.data.json();
  } catch {
    payload = {
      type:  'generic',
      title: 'SPD',
      body:  e.data.text() || 'New notification',
      data:  { url: '/' },
    };
  }

  const { title, body, icon, badge, vibrate, tag, data, type } = payload;

  const options = {
    body:               body  || '',
    icon:               icon  || '/pwa/icons/ios/192.png',
    badge:              badge || '/pwa/icons/ios/72.png',
    vibrate:            vibrate || (type === 'expense'
                          ? [200, 100, 200, 100, 400]
                          : [100, 50,  100, 50,  200]),
    tag:                tag || type || 'spd-notification',
    renotify:           true,
    requireInteraction: false,
    silent:             false,
    timestamp:          Date.now(),
    data:               { ...(data || {}), type },
    actions: type === 'expense'
      ? [{ action: 'view', title: '💸 View Expenses' }]
      : type === 'deposit'
        ? [{ action: 'view', title: '💰 View Balance' }]
        : [{ action: 'view', title: '🔔 View Details' }],
  };

  e.waitUntil(
    Promise.all([
      self.registration.showNotification(title, options),
      self.clients
        .matchAll({ type: 'window', includeUncontrolled: true })
        .then(clients => clients.forEach(c => c.postMessage({ type: 'PUSH_RECEIVED', payload }))),
    ])
  );
});

// ─── Notification Click ───────────────────────────────────────────────────────

self.addEventListener('notificationclick', (e) => {
  e.notification.close();

  const url = e.notification.data?.url || '/';

  e.waitUntil(
    self.clients
      .matchAll({ type: 'window', includeUncontrolled: true })
      .then(clients => {
        const existing = clients.find(c => new URL(c.url).origin === self.location.origin);
        if (existing) {
          return existing.focus().then(w => w.navigate(url));
        }
        return self.clients.openWindow(url);
      })
  );
});

// ─── Subscription Auto-Renewal ────────────────────────────────────────────────

self.addEventListener('pushsubscriptionchange', (e) => {
  e.waitUntil(
    fetch('/push/key')
      .then(r => r.json())
      .then(json => self.registration.pushManager.subscribe({
        userVisibleOnly:      true,
        applicationServerKey: urlBase64ToUint8Array(json.key),
      }))
      .then(sub =>
        fetch('/push/subscribe', {
          method:  'POST',
          headers: { 'Content-Type': 'application/json' },
          body:    JSON.stringify(sub),
        })
      )
      .catch(err => console.error('[SW] Subscription renewal failed:', err))
  );
});

// ─── Utility ─────────────────────────────────────────────────────────────────

function urlBase64ToUint8Array(base64) {
  const padding = '='.repeat((4 - (base64.length % 4)) % 4);
  const b64     = (base64 + padding).replace(/-/g, '+').replace(/_/g, '/');
  const raw     = atob(b64);
  return Uint8Array.from([...raw].map(c => c.charCodeAt(0)));
}
