// src/sw.ts
// Make sure this file is in src/ as configured in vite.config.ts (srcDir & filename)

// Ensure globalThis.skipWaiting and clientsClaim are available in the SW scope
/// <reference lib="webworker" />

declare const self: ServiceWorkerGlobalScope &
  typeof globalThis & {
    skipWaiting: () => Promise<void>;
    __WB_MANIFEST: Array<{ url: string; revision: string | null }>;
  };

import { clientsClaim } from 'workbox-core';
import {
  precacheAndRoute,
  cleanupOutdatedCaches,
} from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { BackgroundSyncPlugin } from 'workbox-background-sync';

// Precache all assets generated by Vite
precacheAndRoute(self.__WB_MANIFEST);

// Clean up outdated caches
cleanupOutdatedCaches();

// Take control of all pages immediately
clientsClaim();

// Background sync plugin
const bgSyncPlugin = new BackgroundSyncPlugin('api-queue', {
  maxRetentionTime: 24 * 60, // Retry for 24 hours
});

// Cache API responses with NetworkFirst strategy
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api-cache',
    networkTimeoutSeconds: 3,
    plugins: [
      {
        cacheKeyWillBeUsed: async ({ request }) => {
          // Create cache keys that ignore auth headers for better cache hits
          const url = new URL(request.url);
          return url.pathname + url.search;
        },
      },
    ],
  })
);

// Cache static assets with CacheFirst strategy
registerRoute(
  ({ request }) =>
    request.destination === 'image' ||
    request.destination === 'font' ||
    request.destination === 'style',
  new CacheFirst({
    cacheName: 'static-assets',
    plugins: [
      {
        cacheWillUpdate: async ({ response }) => {
          return response.status === 200 ? response : null;
        },
      },
    ],
  })
);

// Cache navigation requests with StaleWhileRevalidate
registerRoute(
  ({ request }) => request.mode === 'navigate',
  new StaleWhileRevalidate({
    cacheName: 'navigation-cache',
  })
);

// Register background sync route for API calls
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/') &&
    !url.pathname.includes('/auth/'),
  new NetworkFirst({
    cacheName: 'api-mutations',
    plugins: [bgSyncPlugin],
  }),
  'POST'
);

registerRoute(
  ({ url }) => url.pathname.startsWith('/api/') &&
    !url.pathname.includes('/auth/'),
  new NetworkFirst({
    cacheName: 'api-mutations',
    plugins: [bgSyncPlugin],
  }),
  'PUT'
);

registerRoute(
  ({ url }) => url.pathname.startsWith('/api/') &&
    !url.pathname.includes('/auth/'),
  new NetworkFirst({
    cacheName: 'api-mutations',
    plugins: [bgSyncPlugin],
  }),
  'DELETE'
);

// Handle offline fallback
const OFFLINE_FALLBACK_URL = '/offline.html';

// Cache the offline fallback page
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('offline-fallback')
      .then(cache => cache.add(OFFLINE_FALLBACK_URL))
  );
});

// Serve offline fallback when network fails
self.addEventListener('fetch', (event) => {
  if (event.request.mode === 'navigate') {
    event.respondWith(
      (async () => {
        try {
          return await fetch(event.request);
        } catch {
          const cached = await caches.match(OFFLINE_FALLBACK_URL);
          return cached ?? new Response('Offline', { status: 503, statusText: 'Offline' });
        }
      })()
    );
  }
});

// Listen for messages from main thread
self.addEventListener('message', (event) => {
  if (event.data?.type === 'SYNC_BACKGROUND') {
    // Trigger background sync
    self.registration.sync.register('background-sync');
  }

  if (event.data?.type === 'CLEAR_CACHE') {
    // Clear specific cache
    const cacheName = event.data.cacheName;
    caches.delete(cacheName);
  }

  if (event.data?.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});

// Background sync event
self.addEventListener('sync', (event) => {
  if (event.tag === 'background-sync') {
    console.log('Background sync triggered');
  }
});

// Show notification when app is updated
self.addEventListener('controllerchange', () => {
  // Notify main thread that SW has updated
  self.clients.matchAll().then(clients => {
    clients.forEach(client => {
      client.postMessage({
        type: 'SW_UPDATED',
        message: 'App updated! Refresh to see changes.'
      });
    });
  });
});

// Progressive enhancement: add install prompt handling
// Note: deferredPrompt handling is disabled for now
// let deferredPrompt: any;

self.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  // deferredPrompt = e;

  // Notify main thread that install is available
  self.clients.matchAll().then(clients => {
    clients.forEach(client => {
      client.postMessage({
        type: 'INSTALL_AVAILABLE',
        message: 'App can be installed'
      });
    });
  });
});

// Handle install event
self.addEventListener('install', (event) => {
  console.log('Service Worker installing...');

  // Cache critical resources immediately
  event.waitUntil(
    caches.open('critical-cache-v1').then(cache => {
      return cache.addAll([
        '/',
        '/manifest.json',
        '/offline.html'
      ]);
    })
  );
});

// Handle activation
self.addEventListener('activate', (event) => {
  console.log('Service Worker activated');

  // Clean up old caches
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames
          .filter(cacheName => cacheName.startsWith('critical-cache-') &&
            cacheName !== 'critical-cache-v1')
          .map(cacheName => caches.delete(cacheName))
      );
    })
  );
});

// Export for TypeScript
export default null;