/* Pich2Pich service worker — offline shell + network-first for app updates (iOS PWA) */
const CACHE = 'pich2pich-v123';
const SHELL = [
  './',
  './index.html',
  './download.html',
  './style.css',
  './app.js',
  './features.js',
  './team.js',
  './extras.js',
  './apk-parity.js',
  './offline-mode.js',
  './opus-pich.js',
  './meshtastic.js',
  './stability-v62.js',
  './manifest.webmanifest',
  './icon-192.png',
  './icon-512.png',
  './vendor/maplibre-gl.js',
  './vendor/maplibre-gl.css',
  './vendor/pmtiles.js',
  './vendor/protomaps-basemaps.js',
  './vendor/fflate.min.js',
  './vendor/opus-decoder.min.js',
  './vendor/qrcode.js',
  './vendor/jsqr.min.js',
  './config.defaults.json',
  './chrome-start.html'
];

/** 每次部署必須 network-first，否則 iOS 主畫面會卡在舊版 */
const NETWORK_FIRST = new Set([
  './',
  './index.html',
  './app.js',
  './features.js',
  './team.js',
  './extras.js',
  './offline-mode.js',
  './opus-pich.js',
  './apk-parity.js',
  './meshtastic.js',
  './stability-v62.js',
  './style.css',
  './config.defaults.json',
  './sw.js',
  './manifest.webmanifest'
]);

function normPath(url) {
  const u = new URL(url);
  let p = u.pathname;
  if (p.endsWith('/')) p += 'index.html';
  if (!p.startsWith('./')) {
    const base = u.origin + u.pathname.replace(/\/[^/]*$/, '/');
    p = './' + p.replace(base.replace(u.origin, ''), '').replace(/^\//, '');
  }
  return p;
}

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE).then((cache) => cache.addAll(SHELL)).then(() => self.skipWaiting())
  );
});

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

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') self.skipWaiting();
});

self.addEventListener('fetch', (event) => {
  const req = event.request;
  if (req.method !== 'GET') return;
  const url = new URL(req.url);
  if (url.origin !== self.location.origin) return;

  let path = url.pathname;
  const root = new URL(self.registration.scope).pathname;
  if (root !== '/' && path.startsWith(root)) path = path.slice(root.length - 1);
  if (path === '/' || path === '') path = './index.html';
  else if (!path.startsWith('./')) path = '.' + path;

  const networkFirst = NETWORK_FIRST.has(path) || path.endsWith('.html');

  if (networkFirst) {
    event.respondWith(networkFirstStrategy(req, path));
  } else {
    event.respondWith(cacheFirstStrategy(req, path));
  }
});

async function networkFirstStrategy(request, path) {
  const cache = await caches.open(CACHE);
  try {
    const fresh = await fetch(request, { cache: 'no-store' });
    if (fresh.ok) {
      cache.put(request, fresh.clone());
      return fresh;
    }
  } catch (_) { /* offline */ }
  const cached = await cache.match(request);
  if (cached) return cached;
  return cache.match('./index.html');
}

async function cacheFirstStrategy(request, path) {
  const cache = await caches.open(CACHE);
  const cached = await cache.match(request);
  if (cached) return cached;
  try {
    const fresh = await fetch(request);
    if (fresh.ok) cache.put(request, fresh.clone());
    return fresh;
  } catch (_) {
    return cache.match('./index.html');
  }
}
