// Service worker for the 7kaa web port.
//
// Design (each rule exists because the opposite broke Firefox in production):
//  - 7kaa.data (~107MB): NEVER intercepted, NEVER stream-teed. Teeing it
//    (resp.clone() for cache.put during the live download) overran Firefox's
//    Cache-Storage quota at ~50MB and aborted the download ("Error in input
//    stream"). The page's chunked loader downloads it in 4MB ranges and, after
//    success, stores the COMPLETE buffer itself in DATA_CACHE (one controlled
//    put, no tee) — that cache entry is what we must KEEP here.
//  - Navigations: served network-first WITHOUT cloning the navigation response
//    (Firefox rejects that clone). The offline copy of the HTML comes from a
//    SEPARATE cache.add() request instead.
//  - Small shell files (js/json/png) + the 2.8MB wasm: network-first with
//    synchronous clone (small bodies are safe to tee), cache fallback for
//    offline. Online this costs no extra network: the immutable HTTP cache
//    answers the fetch from disk.
var SHELL_VERSION = 'v3';
var SHELL_CACHE = '7kaa-shell-' + SHELL_VERSION;
var DATA_CACHE  = '7kaa-data-v1';        // written by the page (complete-buffer put), only read here for KEEP
var KEEP = [SHELL_CACHE, DATA_CACHE];    // old '7kaa-assets-*' / '7kaa-shell-v1/2' (may hold corrupt partials) purged on activate

var DATA  = /\/7kaa\.data$/;
var SHELL = /\.(js|json|png|wasm)$/;

self.addEventListener('install', function(e){ self.skipWaiting(); });

self.addEventListener('activate', function(e){
  e.waitUntil(
    caches.keys().then(function(keys){
      return Promise.all(keys.filter(function(k){ return k.indexOf('7kaa-')===0 && KEEP.indexOf(k)<0; })
                             .map(function(k){ return caches.delete(k); }));
    }).then(function(){ return self.clients.claim(); })
  );
});

self.addEventListener('fetch', function(e){
  if (e.request.method !== 'GET') return;
  var path = e.request.url.split('?')[0];

  // Never touch the parallel /v1/ build — it has its own (older) service worker
  // and must behave exactly like a standalone page.
  if (path.indexOf('/7kaa/v1/') >= 0) return;

  // Navigations: network-first; offline -> cached shell. The cached copy is
  // refreshed via a SEPARATE cache.add (no clone of the navigation response —
  // Firefox rejects that with "body stream" errors).
  if (e.request.mode === 'navigate') {
    e.respondWith(
      fetch(e.request).then(function(resp){
        if (resp && resp.ok) {
          caches.open(SHELL_CACHE).then(function(c){ c.add('7kaa.html').catch(function(){}); });
        }
        return resp;
      }).catch(function(){
        return caches.open(SHELL_CACHE).then(function(c){
          return c.match('7kaa.html', {ignoreSearch:true}).then(function(hit){
            return hit || c.match(e.request, {ignoreSearch:true});
          });
        });
      })
    );
    return;
  }

  // 7kaa.data: DO NOT intercept (see header). The page handles caching itself.
  if (DATA.test(path)) return;

  // shell sub-resources + wasm: network-first, cache fallback (offline)
  if (SHELL.test(path)) {
    e.respondWith(
      fetch(e.request).then(function(resp){
        if (resp && resp.ok) {
          var copy = resp.clone();              // clone synchronously, before the page reads the body
          caches.open(SHELL_CACHE).then(function(c){ c.put(e.request, copy).catch(function(){}); });
        }
        return resp;
      }).catch(function(){
        return caches.open(SHELL_CACHE).then(function(c){ return c.match(e.request); });
      })
    );
    return;
  }
  // everything else: default network handling
});
