// Service Worker SAR
// C4/NFR-2: cache de API para uso offline (network-first, fallback to cache)
// C6: Web Push
// App shell: stale-while-revalidate para assets estáticos

const API_CACHE = 'sar-api-v2';
const SHELL_CACHE = 'sar-shell-v2';

// Paths de API que valem ser cacheados para offline
// Auth e mutations (POST/PATCH) nunca são interceptados
const CACHEABLE_API = [
  '/api/v1/clients',
  '/api/v1/catalog',
  '/api/v1/orders',
  '/api/v1/dashboard',
  '/api/v1/auth/me',
];

// ── Fetch ──────────────────────────────────────────────────────────────────────

globalThis.addEventListener('fetch', (event) => {
  const { request } = event;
  if (request.method !== 'GET') return;

  const url = new URL(request.url);

  if (url.pathname.startsWith('/api/v1/')) {
    const cacheable = CACHEABLE_API.some((p) => url.pathname.startsWith(p));
    if (cacheable) {
      event.respondWith(networkFirst(request, API_CACHE));
    }
    return;
  }

  if (request.mode === 'navigate') {
    // App shell HTML — network first, cache fallback
    event.respondWith(shellNavigate(request));
    return;
  }

  // Assets estáticos (JS/CSS/fontes/imagens) — stale-while-revalidate
  if (/\.(js|css|woff2?|png|svg|ico)$/.test(url.pathname)) {
    event.respondWith(staleWhileRevalidate(request, SHELL_CACHE));
  }
});

// Network-first: tenta rede, cai no cache se offline
async function networkFirst(request, cacheName) {
  const cache = await caches.open(cacheName);
  try {
    const response = await fetch(request);
    if (response.ok) {
      cache.put(request, response.clone());
    }
    return response;
  } catch {
    const cached = await cache.match(request);
    if (cached) return cached;
    return offlineResponse();
  }
}

// Navigate: network first; fallback para a raiz cacheada
async function shellNavigate(request) {
  const cache = await caches.open(SHELL_CACHE);
  try {
    const response = await fetch(request);
    if (response.ok) {
      cache.put(request, response.clone());
      // Sempre armazena a raiz como fallback universal
      cache.put(new Request('/'), response.clone());
    }
    return response;
  } catch {
    const cached = (await cache.match(request)) ?? (await cache.match('/'));
    if (cached) return cached;
    return offlineResponse();
  }
}

// Stale-while-revalidate: responde do cache, atualiza em background
async function staleWhileRevalidate(request, cacheName) {
  const cache = await caches.open(cacheName);
  const cached = await cache.match(request);
  const networkFetch = fetch(request).then((response) => {
    if (response.ok) cache.put(request, response.clone());
    return response;
  });
  return cached ?? networkFetch;
}

function offlineResponse() {
  return new Response(
    JSON.stringify({
      type: 'sar:offline',
      title: 'Sem conexão',
      status: 503,
    }),
    { status: 503, headers: { 'Content-Type': 'application/json' } },
  );
}

// ── Push (C6) ─────────────────────────────────────────────────────────────────

globalThis.addEventListener('push', (event) => {
  const data = event.data?.json() ?? {};
  event.waitUntil(
    globalThis.registration.showNotification(data.title ?? 'SAR', {
      body: data.body ?? '',
      icon: '/sar-icon.png',
      badge: '/sar-icon.png',
      data: data.url ? { url: data.url } : undefined,
    }),
  );
});

globalThis.addEventListener('notificationclick', (event) => {
  event.notification.close();
  const url = event.notification.data?.url;
  if (url) {
    event.waitUntil(clients.openWindow(url));
  }
});
