/**
 * Inventory Counter PWA — Service Worker (v1)
 * Scope: /invcount/
 * Strategies:
 *   CacheFirst  — CSS, JS, fonts (speed)
 *   CacheFirst  — Images (speed)
 *   NetworkFirst — PHP pages & API (freshness, <siteurl> PHP pages & API (freshness, with session cookie)
 *   StaleWhileRevalidate — Dashboard/app shell (speed + freshness balance)
 */
'use strict';

const CACHE_NAMES = {
  core:    'invcount-core-v1',
  dynamic: 'invcount-dynamic-v1',
  images:  'invcount-images-v1',
  fonts:   'invcount-fonts-v1',
};

// ── Install ────────────────────────────────────────────────────────────────────
self.addEventListener('install', event => {
  console.info('[SW] Install event.');
  event.waitUntil(
    (async () => {
      // Defensive: force-enable navigationPreload if supported
      if ('navigationPreload' in self.registration) {
        await self.registration.navigationPreload.enable();
      }
      const cache = await caches.open(CACHE_NAMES.core);
      console.info('[SW] Precaching app core shell…');
      await cache.addAll([
        '/invcount/login.php',
        '/invcount/dashboard.php',
        '/invcount/css/style.css',
        '/invcount/css/responsive.css',
        '/invcount/js/register-sw.js',
        '/invcount/js/items_datatables.js',
        '/invcount/images/icons/icon-192x192.png',
        '/invcount/images/icons/icon-512x512.png',
        '/invcount/offline.html',
      ]);
      console.info('[SW] Core shell precached.');
      self.skipWaiting();
    })()
  );
});

// ── Activate ───────────────────────────────────────────────────────────────────
self.addEventListener('activate', event => {
  console.info('[SW] Activate event.');
  event.waitUntil(
    (async () => {
      const allNames = await caches.keys();
      const toDelete = allNames.filter(
        name => !Object.values(CACHE_NAMES).includes(name)
      );
      await Promise.all(toDelete.map(name => caches.delete(name)));
      console.info('[SW] Purged', toDelete.length, 'obsolete cache(s).');
      await self.clients.claim();
      self.__activeVersion = CACHE_NAMES.core;
    })()
  );
});

// ── Fetch ──────────────────────────────────────────────────────────────────────
self.addEventListener('fetch', event => {
  const { request } = event;
  const url = new URL(request.url);

  // 1. Only GET requests — pass through POST/PUT/DELETE/OPTIONS
  if (request.method !== 'GET') return;

  // 2. Cross-origin requests — pass through (CDN fonts, DataTables CDN, etc.)
  if (url.origin !== self.location.origin) return;

  // 3. Route by file type / path
  const path = url.pathname.replace(/^\/invcount/, '') || '/';
  const originBase = '/invcount';

  if (path.match(/\.(css|js|woff2?|ttf|eot)$/i)) {
    // Static JS/CSS/fonts → CacheFirst
    event.respondWith(cacheFirst(request, CACHE_NAMES.core));
  } else if (path.match(/\.(png|jpe?g|webp|avif|svg|gif|ico)$/i)) {
    // Images → CacheFirst
    event.respondWith(cacheFirst(request, CACHE_NAMES.images));
  } else if (path.match(/\/fonts\//i)) {
    // Web fonts → CacheFirst
    event.respondWith(cacheFirst(request, CACHE_NAMES.fonts));
  } else if (
    path.startsWith('/api/') ||
    path.startsWith('/ajax/') ||
    path.includes('api_locations.php') ||
    path.includes('api_') ||
    path.match(/\.php\?.*action=/)
  ) {
    // API / ajax PHP endpoints → NetworkFirst (freshness matters)
    event.respondWith(networkFirst(request, CACHE_NAMES.dynamic, 300));
  } else if (/^\/invcount\/dashboard\.php/.test(request.url) || path === '/') {
    // Dashboard shell → StaleWhileRevalidate (instant paint + background refresh)
    event.respondWith(staleWhileRevalidate(request, CACHE_NAMES.core));
  } else {
    // All other PHP pages (sessions, assignments, items, reports…) → NetworkFirst
    // Never cache auth-protected PHP output — session-specific data must stay fresh
    event.respondWith(networkFirst(request, CACHE_NAMES.core));
  }
});

// ── Helpers ────────────────────────────────────────────────────────────────────

async function cacheFirst(request, cacheName) {
  const cache = await caches.open(cacheName);
  const cached = await cache.match(request);
  if (cached) return cached;
  try {
    const response = await fetch(request);
    if (response && response.status === 200) {
      await cache.put(request, response.clone());
    }
    return response;
  } catch {
    return new Response('Asset unavailable offline', {
      status: 503,
      headers: { 'Content-Type': 'text/plain' },
    });
  }
}

async function networkFirst(request, cacheName, maxAgeMs) {
  const cache = await caches.open(cacheName);
  try {
    const response = await fetch(request, { credentials: 'same-origin' });
    if (response && response.status === 200) {
      await cache.put(request, response.clone());
    }
    return response;
  } catch {
    const cached = await cache.match(request);
    if (cached) return cached;
    // Fall back to offline page if no cache
    const offline = await caches.match('/invcount/offline.html');
    return offline || new Response('Offline — reconnect to continue.', {
      status: 503,
      headers: { 'Content-Type': 'text/plain' },
    });
  }
}

async function staleWhileRevalidate(request, cacheName) {
  const cache = await caches.open(cacheName);
  const cached = await cache.match(request);
  const fetchPromise = fetch(request).then(response => {
    if (response && response.status === 200) {
      cache.put(request, response.clone());
    }
    return response;
  }).catch(() => undefined);
  return cached || fetchPromise;
}

// ── Push / Background Sync hooks ───────────────────────────────────────────────

self.addEventListener('push', event => {
  if (!event.data) return;
  try {
    const data = event.data.json();
    self.registration.showNotification(data.title ?? 'Inventory Counter', {
      body:   data.body ?? 'New activity recorded.',
      icon:   '/invcount/images/icons/icon-192x192.png',
      badge:  '/invcount/images/icons/icon-72x72.png',
      data:   data.url ?? '/invcount/login.php',
      tag:    'invcount-notification',
      requireInteraction: true,
    });
  } catch {
    // Push payload wasn't JSON — show a generic notification
    self.registration.showNotification('Inventory Counter', {
      body:  event.data.text(),
      icon:  '/invcount/images/icons/icon-192x192.png',
      tag:   'invcount-notification',
    });
  }
});

self.addEventListener('notificationclick', event => {
  event.notification.close();
  const url = event.notification.data || '/invcount/dashboard.php';
  event.waitUntil(
    clients.matchAll({ type: 'window', includeUncontrolled: true })
      .then(list => {
        for (const client of list) {
          if (client.url === url && 'focus' in client) return client.focus();
        }
        return clients.openWindow(url);
      })
  );
});

// ── SW messaging ───────────────────────────────────────────────────────────────
self.addEventListener('message', event => {
  if (event.data === 'SKIP_WAITING') self.skipWaiting();
});
