/**
 * WorkRoot IT Solutions - Service Worker
 * Implements:
 * - Cache-first strategy for static assets
 * - Stale-while-revalidate for HTML pages
 * - Offline fallback page
 * - Background sync for form submissions
 */

const CACHE_VERSION = 'v1';
const STATIC_CACHE = `workroot-static-${CACHE_VERSION}`;
const PAGE_CACHE = `workroot-pages-${CACHE_VERSION}`;
const SYNC_QUEUE = 'workroot-sync-queue';

// Static assets to pre-cache on install
const PRECACHE_ASSETS = [
  '/',
  '/offline',
  '/manifest.json',
  '/favicon.svg',
  '/apple-touch-icon.png',
];

// Page routes to cache with stale-while-revalidate
const PAGE_ROUTES = [
  '/',
  '/about',
  '/services',
  '/portfolio',
  '/contact',
  '/blog',
  '/privacy',
  '/terms',
];

// ─── Install Event ────────────────────────────────────────────────────────────

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

// ─── Activate Event ───────────────────────────────────────────────────────────

self.addEventListener('activate', (event) => {
  event.waitUntil(
    Promise.all([
      // Delete old caches
      caches.keys().then((cacheNames) => {
        return Promise.all(
          cacheNames
            .filter((name) => name.startsWith('workroot-') && name !== STATIC_CACHE && name !== PAGE_CACHE)
            .map((name) => caches.delete(name))
        );
      }),
      // Take control immediately
      self.clients.claim(),
    ])
  );
});

// ─── Fetch Event ──────────────────────────────────────────────────────────────

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

  // Only handle same-origin requests
  if (url.origin !== self.location.origin) return;

  // Skip non-GET requests (POST etc. handled via background sync)
  if (request.method !== 'GET') return;

  // Skip API routes — always go to network
  if (url.pathname.startsWith('/api/')) return;

  // Static assets: cache-first
  if (isStaticAsset(url.pathname)) {
    event.respondWith(cacheFirst(request, STATIC_CACHE));
    return;
  }

  // HTML pages: stale-while-revalidate with offline fallback
  if (request.headers.get('Accept')?.includes('text/html')) {
    event.respondWith(staleWhileRevalidate(request));
    return;
  }
});

// ─── Background Sync Event ───────────────────────────────────────────────────

self.addEventListener('sync', (event) => {
  if (event.tag === 'form-sync') {
    event.waitUntil(processSyncQueue());
  }
});

// ─── Push Event (for future notifications) ──────────────────────────────────

self.addEventListener('push', (event) => {
  if (!event.data) return;

  const data = event.data.json();
  event.waitUntil(
    self.registration.showNotification(data.title || 'WorkRoot', {
      body: data.body || '',
      icon: '/apple-touch-icon.png',
      badge: '/favicon.svg',
      data: data.url ? { url: data.url } : undefined,
    })
  );
});

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

// ─── Strategy Helpers ─────────────────────────────────────────────────────────

function isStaticAsset(pathname) {
  return (
    pathname.startsWith('/_assets/') ||
    /\.(js|css|woff2?|ttf|otf|eot|png|jpg|jpeg|gif|webp|avif|svg|ico)$/i.test(pathname)
  );
}

async function cacheFirst(request, cacheName) {
  const cache = await caches.open(cacheName);
  const cached = await cache.match(request);
  if (cached) return cached;

  try {
    const response = await fetch(request);
    if (response.ok) {
      cache.put(request, response.clone());
    }
    return response;
  } catch {
    return new Response('Asset unavailable offline', { status: 503 });
  }
}

async function staleWhileRevalidate(request) {
  const cache = await caches.open(PAGE_CACHE);
  const cached = await cache.match(request);

  const fetchPromise = fetch(request)
    .then((response) => {
      if (response.ok) {
        cache.put(request, response.clone());
      }
      return response;
    })
    .catch(() => null);

  if (cached) {
    // Serve cached immediately, update in background
    fetchPromise.catch(() => {});
    return cached;
  }

  // No cache — await network
  const response = await fetchPromise;
  if (response) return response;

  // Network failed and no cache — serve offline fallback
  const offlineFallback = await cache.match('/offline') || await caches.match('/offline');
  if (offlineFallback) return offlineFallback;

  return new Response(
    '<html><body><h1>You are offline</h1><p>Please check your connection.</p></body></html>',
    { status: 503, headers: { 'Content-Type': 'text/html' } }
  );
}

// ─── Background Sync Queue ────────────────────────────────────────────────────

async function processSyncQueue() {
  const db = await openDB();
  const queue = await getAllFromDB(db, SYNC_QUEUE);

  for (const item of queue) {
    try {
      const response = await fetch(item.url, {
        method: item.method,
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(item.body),
      });

      if (response.ok) {
        await deleteFromDB(db, SYNC_QUEUE, item.id);
        // Notify any open clients of success
        const clients = await self.clients.matchAll();
        clients.forEach((client) => {
          client.postMessage({ type: 'SYNC_SUCCESS', id: item.id, formType: item.formType });
        });
      }
    } catch {
      // Leave in queue for next sync attempt
    }
  }
}

// ─── IndexedDB Helpers ────────────────────────────────────────────────────────

function openDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open('workroot-pwa', 1);

    request.onupgradeneeded = (event) => {
      const db = event.target.result;
      if (!db.objectStoreNames.contains(SYNC_QUEUE)) {
        const store = db.createObjectStore(SYNC_QUEUE, { keyPath: 'id', autoIncrement: true });
        store.createIndex('timestamp', 'timestamp', { unique: false });
      }
    };

    request.onsuccess = (event) => resolve(event.target.result);
    request.onerror = () => reject(request.error);
  });
}

function getAllFromDB(db, storeName) {
  return new Promise((resolve, reject) => {
    const tx = db.transaction(storeName, 'readonly');
    const store = tx.objectStore(storeName);
    const request = store.getAll();
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
  });
}

function deleteFromDB(db, storeName, id) {
  return new Promise((resolve, reject) => {
    const tx = db.transaction(storeName, 'readwrite');
    const store = tx.objectStore(storeName);
    const request = store.delete(id);
    request.onsuccess = () => resolve();
    request.onerror = () => reject(request.error);
  });
}
