const CACHE_VERSION = typeof __APP_CACHE_VERSION__ !== 'undefined' 
    ? __APP_CACHE_VERSION__ 
    : 'nexus-timer-cache-fallback-dev-vManual';

const APP_SHELL_URLS = [
  // Precache the root (index.html) explicitly for better offline fallback
  '/', 
  '/manifest.json',
  '/favicon.ico',
  '/icons/icon-192x192.png',
  '/icons/icon-512x512.png',
  '/icons/maskable-icon-192x192.png',
  '/icons/maskable-icon-512x512.png',
  '/icons/shortcut-setup-96x96.png',
  '/icons/shortcut-info-96x96.png',
];

self.addEventListener('install', event => {
  console.log(`[SW ${CACHE_VERSION}] Install`);
  event.waitUntil(
    caches.open(CACHE_VERSION)
      .then(cache => {
        console.log(`[SW ${CACHE_VERSION}] Caching app shell essentials`);
        return cache.addAll(APP_SHELL_URLS);
      })
      .then(() => {
        console.log(`[SW ${CACHE_VERSION}] Skip waiting on install.`);
        return self.skipWaiting();
      })
  );
});

self.addEventListener('activate', event => {
  console.log(`[SW ${CACHE_VERSION}] Activate`);
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== CACHE_VERSION) {
            console.log(`[SW ${CACHE_VERSION}] Deleting old cache: ${cacheName}`);
            return caches.delete(cacheName);
          }
        })
      );
    }).then(() => {
      console.log(`[SW ${CACHE_VERSION}] Clients claimed.`);
      return self.clients.claim();
    })
  );
});

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

  if (request.method !== 'GET' || !url.protocol.startsWith('http')) {
    return;
  }

  // Strategy 1: Network First, then Cache for Navigation/HTML requests
  if (request.mode === 'navigate' || request.destination === 'document' || url.pathname === '/') {
    // console.log(`[SW ${CACHE_VERSION}] NetworkFirst for navigation/document: ${request.url}`);
    event.respondWith(
      fetch(request)
        .then(networkResponse => {
          // If successful, cache the response and return it
          if (networkResponse.ok) {
            const responseToCache = networkResponse.clone();
            caches.open(CACHE_VERSION).then(cache => {
              // For navigations, it's often best to cache the specific URL requested
              // as well as potentially updating the '/' cache if this is the root.
              cache.put(request, responseToCache);
              if (url.pathname === '/') { // Also update root cache if it's the index
                  const rootResponseClone = networkResponse.clone(); // Need another clone
                  cache.put('/', rootResponseClone);
              }
            });
          }
          return networkResponse;
        })
        .catch(async () => {
          // Network failed. Try to serve from cache.
          // console.warn(`[SW ${CACHE_VERSION}] Network fetch failed for ${request.url}. Attempting cache.`);
          
          // 1. Try matching the specific request first (e.g. /info, /game)
          const cachedResponse = await caches.match(request);
          if (cachedResponse) {
            // console.log(`[SW ${CACHE_VERSION}] Serving from cache (specific request): ${request.url}`);
            return cachedResponse;
          }

          // 2. If specific request not found, try serving the app shell ('/')
          // This is crucial for SPAs to work offline.
          const appShellResponse = await caches.match('/');
          if (appShellResponse) {
            // console.log(`[SW ${CACHE_VERSION}] Serving app shell ('/') from cache for: ${request.url}`);
            return appShellResponse;
          }

          // 3. If even the app shell is not in cache (shouldn't happen if install was successful)
          console.error(`[SW ${CACHE_VERSION}] CRITICAL: Network and cache miss for navigation AND app shell ('/') for: ${request.url}`);
          // Return a very basic offline message, but ideally this state is avoided.
          return new Response(
            `<h1>Offline</h1><p>The application is currently offline and the requested page could not be loaded from the cache. Please check your connection.</p>`, 
            { headers: { 'Content-Type': 'text/html' } }
          );
        })
    );
    return;
  }

  // Strategy 2: Stale-While-Revalidate for assets (CSS, JS, images, fonts, workers)
  if (request.destination === 'style' ||
      request.destination === 'script' ||
      request.destination === 'worker' ||
      request.destination === 'image' ||
      request.destination === 'font') {
    // console.log(`[SW ${CACHE_VERSION}] StaleWhileRevalidate for asset: ${request.url}`);
    event.respondWith(
      caches.open(CACHE_VERSION).then(cache => {
        return cache.match(request).then(cachedResponse => {
          const fetchPromise = fetch(request).then(networkResponse => {
            if (networkResponse.ok) {
              const responseToCache = networkResponse.clone();
              cache.put(request, responseToCache);
            }
            return networkResponse;
          }).catch(err => {
            // If fetch fails, and we served from cache, it's fine.
            // If cache also missed, this error will propagate.
            // console.warn(`[SW ${CACHE_VERSION}] SWR: Network fetch error for ${request.url}`, err);
            throw err;
          });
          return cachedResponse || fetchPromise;
        }).catch(() => {
            // Fallback to network if cache.match fails
            // console.warn(`[SW ${CACHE_VERSION}] SWR: Cache match error for ${request.url}, trying network directly.`);
            return fetch(request);
        });
      })
    );
    return;
  }

  // Strategy 3: Cache First for other types of requests (e.g., manifest.json if not in APP_SHELL_URLS)
  // console.log(`[SW ${CACHE_VERSION}] CacheFirst for: ${request.url}`);
  event.respondWith(
    caches.match(request)
      .then(response => {
        return response || fetch(request).then(networkResponse => {
            if(networkResponse.ok) {
                const responseClone = networkResponse.clone();
                caches.open(CACHE_VERSION).then(cache => cache.put(request, responseClone));
            }
            return networkResponse;
        });
      })
  );
});

self.addEventListener('message', event => {
  if (event.data && event.data.action === 'skipWaiting') {
    console.log(`[SW ${CACHE_VERSION}] Received skipWaiting message, activating new SW.`);
    self.skipWaiting();
  }
});