/* every.channel PWA service worker
 *
 * Goal: cache the app shell so it can be installed and load offline.
 * Do not interfere with media fetching/streaming: always network-pass-through
 * for non-GET requests and for large binary media responses.
 */

const CACHE_NAME = "every.channel-shell-v1";
const SHELL = [
  "./",
  "./index.html",
  "./style.css",
  "./manifest.webmanifest",
  "./icons/icon-192.png",
  "./icons/icon-512.png",
  "./icons/apple-touch-icon.png",
];

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

self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches
      .keys()
      .then((keys) =>
        Promise.all(
          keys.map((key) => {
            if (key !== CACHE_NAME) return caches.delete(key);
            return Promise.resolve();
          })
        )
      )
      .then(() => self.clients.claim())
  );
});

function isNavigationRequest(request) {
  return request.mode === "navigate";
}

function isMediaRequest(request) {
  const url = new URL(request.url);
  const path = url.pathname.toLowerCase();
  return (
    path.endsWith(".m3u8") ||
    path.endsWith(".m4s") ||
    path.endsWith(".mp4") ||
    path.endsWith(".ts")
  );
}

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

  // Don't cache/modify streaming media requests.
  if (isMediaRequest(request)) {
    event.respondWith(fetch(request));
    return;
  }

  // For navigations, prefer network but fall back to cached shell.
  if (isNavigationRequest(request)) {
    event.respondWith(
      fetch(request).catch(() => caches.match("./index.html").then((r) => r || Response.error()))
    );
    return;
  }

  // Cache-first for same-origin static assets; network fallback.
  const url = new URL(request.url);
  if (url.origin === self.location.origin) {
    event.respondWith(
      caches.match(request).then((cached) => {
        if (cached) return cached;
        return fetch(request)
          .then((resp) => {
            // Avoid caching huge binary responses.
            const len = resp.headers.get("content-length");
            const tooBig = len && Number(len) > 5_000_000;
            if (resp.ok && !tooBig) {
              const clone = resp.clone();
              caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)).catch(() => {});
            }
            return resp;
          })
          .catch(() => cached || Response.error());
      })
    );
    return;
  }

  // Default: network.
  event.respondWith(fetch(request));
});

