/* eslint-disable no-restricted-globals */

// OneUptime Progressive Web App Service Worker
// Handles push notifications and caching for PWA functionality

console.log('[ServiceWorker] OneUptime PWA Service Worker Loaded');

// Cache configuration - Updated dynamically during build
// Version format: oneuptime-v{APP_VERSION}-{GIT_SHA}
// This ensures cache invalidation on every deployment
const CACHE_VERSION = 'oneuptime-v{{APP_VERSION}}-{{GIT_SHA}}'; // Auto-generated version
const STATIC_CACHE = `${CACHE_VERSION}-static`;
const DYNAMIC_CACHE = `${CACHE_VERSION}-dynamic`;

// Cache duration configuration (in milliseconds)
const CACHE_DURATIONS = {
  static: 7 * 24 * 60 * 60 * 1000, // 7 days for static assets
  dynamic: 24 * 60 * 60 * 1000,    // 1 day for dynamic content
};

// Assets to cache immediately during install
const STATIC_ASSETS = [
  '/dashboard/',
  '/dashboard/manifest.json',
  '/dashboard/offline.html',
  '/dashboard/assets/img/favicons/favicon.ico',
  '/dashboard/assets/img/favicons/android-chrome-192x192.png',
  '/dashboard/assets/img/favicons/android-chrome-512x512.png',
  // Add other critical assets as needed
];

// Install event - cache static assets
self.addEventListener('install', function(event) {
  console.log('[ServiceWorker] Installing...');
  
  event.waitUntil(
    Promise.all([
      // Cache static assets
      caches.open(STATIC_CACHE).then(function(cache) {
        console.log('[ServiceWorker] Pre-caching static assets');
        return cache.addAll(STATIC_ASSETS.filter(url => url !== '/dashboard/'));
      }),
      
      // Skip waiting to activate immediately
      self.skipWaiting()
    ])
  );
});

// Activate event - clean up old caches
self.addEventListener('activate', function(event) {
  console.log('[ServiceWorker] Activating...');
  
  event.waitUntil(
    Promise.all([
      // Clean up old caches
      caches.keys().then(function(cacheNames) {
        return Promise.all(
          cacheNames.map(function(cacheName) {
            if (cacheName.startsWith('oneuptime-') && 
                !cacheName.startsWith(CACHE_VERSION)) {
              console.log('[ServiceWorker] Deleting old cache:', cacheName);
              return caches.delete(cacheName);
            }
          })
        );
      }),
      
      // Claim all clients
      self.clients.claim()
    ])
  );
});

// Fetch event - implement caching strategies
self.addEventListener('fetch', function(event) {
  const request = event.request;
  const url = new URL(request.url);
  
  // Skip non-GET requests
  if (request.method !== 'GET') {
    return;
  }
  
  // Skip chrome-extension and other non-http(s) requests
  if (!url.protocol.startsWith('http')) {
    return;
  }
  
  event.respondWith(handleRequest(request));
});

// Request handling with different caching strategies
async function handleRequest(request) {
  const url = new URL(request.url);
  const pathname = url.pathname;
  
  try {
    // Strategy 1: Network First for HTML pages (with fallback)
    if (pathname.endsWith('/') || pathname.endsWith('.html') || 
        pathname === '/dashboard' || pathname.startsWith('/dashboard/') && !pathname.includes('.')) {
      return await networkFirstWithFallback(request, DYNAMIC_CACHE);
    }
    
    // Strategy 2: Cache First for JavaScript, CSS, and other static assets
    if (pathname.includes('/dist/') || pathname.match(/\.(js|css|woff|woff2|ttf|otf|eot)$/)) {
      return await cacheFirstWithUpdate(request, STATIC_CACHE);
    }
    
    // Strategy 3: Cache First for images and other media
    if (pathname.match(/\.(png|jpe?g|gif|svg|ico|webp|avif)$/)) {
      return await cacheFirstWithUpdate(request, STATIC_CACHE);
    }
  
    
    // Strategy 5: Network First for everything else
    return await networkFirstWithFallback(request, DYNAMIC_CACHE);
    
  } catch (error) {
    console.error('[ServiceWorker] Request handling error:', error);
    
    // Return offline page for navigation requests
    if (request.mode === 'navigate') {
      const offlineResponse = await caches.match('/dashboard/offline.html');
      if (offlineResponse) {
        return offlineResponse;
      }
    }
    
    // Return a basic offline response
    return new Response('Offline - Please check your internet connection', {
      status: 503,
      statusText: 'Service Unavailable',
      headers: { 'Content-Type': 'text/plain' }
    });
  }
}

// Caching Strategy 1: Network First with Fallback (for HTML)
async function networkFirstWithFallback(request, cacheName) {
  try {
    const networkResponse = await fetch(request);
    
    if (networkResponse.ok) {
      // Cache successful responses
      const cache = await caches.open(cacheName);
      cache.put(request, networkResponse.clone());
    }
    
    return networkResponse;
  } catch (error) {
    console.log('[ServiceWorker] Network failed, trying cache:', request.url);
    
    const cachedResponse = await caches.match(request);
    if (cachedResponse) {
      return cachedResponse;
    }
    
    throw error;
  }
}

// Caching Strategy 2: Cache First with Background Update (for static assets)
async function cacheFirstWithUpdate(request, cacheName) {
  const cachedResponse = await caches.match(request);
  
  if (cachedResponse) {
    // Return cached version immediately
    
    // Background update if cache is old
    const cacheDate = new Date(cachedResponse.headers.get('date') || 0);
    const now = new Date();
    const age = now.getTime() - cacheDate.getTime();
    
    if (age > CACHE_DURATIONS.static) {
      // Background update - don't await
      updateCacheInBackground(request, cacheName);
    }
    
    return cachedResponse;
  }
  
  // Not in cache, fetch from network
  try {
    const networkResponse = await fetch(request);
    
    if (networkResponse.ok) {
      const cache = await caches.open(cacheName);
      cache.put(request, networkResponse.clone());
    }
    
    return networkResponse;
  } catch (error) {
    console.error('[ServiceWorker] Failed to fetch asset:', request.url, error);
    throw error;
  }
}

// Background cache update
async function updateCacheInBackground(request, cacheName) {
  try {
    const networkResponse = await fetch(request);
    
    if (networkResponse.ok) {
      const cache = await caches.open(cacheName);
      await cache.put(request, networkResponse);
      console.log('[ServiceWorker] Background cache update:', request.url);
    }
  } catch (error) {
    console.log('[ServiceWorker] Background update failed:', request.url, error);
  }
}

// Handle push subscription changes
self.addEventListener('pushsubscriptionchange', function(event) {
  console.log('[ServiceWorker] Push subscription changed:', event);
  
  // Re-subscribe to push notifications
  event.waitUntil(
    self.registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: null // This should be set by your application
    })
    .then(function(newSubscription) {
      console.log('[ServiceWorker] New push subscription:', newSubscription);
      // Send new subscription to your server
      return updatePushSubscription(newSubscription);
    })
    .catch(function(error) {
      console.error('[ServiceWorker] Failed to resubscribe to push:', error);
    })
  );
});

// Handle push notifications
self.addEventListener('push', function(event) {
  console.log('[ServiceWorker] Push received:', event);
  console.log('[ServiceWorker] Event data available:', !!event.data);
  
  if (event.data) {
    try {
      const dataText = event.data.text();
      console.log('[ServiceWorker] Raw push data:', dataText);
      
      const data = event.data.json();
      console.log('[ServiceWorker] Push data (parsed):', data);
      
      const options = {
        body: data.body,
        icon: data.icon || '/dashboard/assets/img/favicons/android-chrome-192x192.png',
        badge: data.badge || '/dashboard/assets/img/favicons/favicon-32x32.png',
        tag: data.tag || 'oneuptime-notification',
        requireInteraction: data.requireInteraction || false,
        actions: data.actions || [],
        data: data.data || {},
        silent: false,
        renotify: true,
        vibrate: [100, 50, 100],
        timestamp: Date.now()
      };

      console.log('[ServiceWorker] Showing notification with options:', options);

      event.waitUntil(
        self.registration.showNotification(data.title, options)
          .then(() => {
            console.log('[ServiceWorker] Notification shown successfully');
          })
          .catch((error) => {
            console.error('[ServiceWorker] Error showing notification:', error);
          })
      );
    } catch (error) {
      console.error('[ServiceWorker] Error parsing push data:', error);
      const rawData = event.data ? event.data.text() : 'No data';
      console.log('[ServiceWorker] Raw event data:', rawData);
      
      // Show fallback notification
      event.waitUntil(
        self.registration.showNotification('OneUptime Notification', {
          body: 'You have a new notification from OneUptime',
          icon: '/dashboard/assets/img/favicons/android-chrome-192x192.png',
          tag: 'oneuptime-fallback',
          data: { url: '/dashboard' }
        })
      );
    }
  } else {
    console.log('[ServiceWorker] Push event received but no data');
    
    // Show default notification
    event.waitUntil(
      self.registration.showNotification('OneUptime', {
        body: 'You have a new notification',
        icon: '/dashboard/assets/img/favicons/android-chrome-192x192.png',
        tag: 'oneuptime-default',
        data: { url: '/dashboard' }
      })
    );
  }
});

// Handle notification clicks
self.addEventListener('notificationclick', function(event) {
  console.log('[ServiceWorker] Notification clicked:', event.notification.tag);
  event.notification.close();

  const clickAction = event.action;
  const notificationData = event.notification.data || {};
  
  let targetUrl = '/dashboard';

  if (clickAction && notificationData[clickAction]) {
    // Handle action button clicks
    targetUrl = notificationData[clickAction].url || targetUrl;
  } else {
    // Handle main notification click
    targetUrl = notificationData.url || 
                notificationData.clickAction || 
                notificationData.link ||
                targetUrl;
  }

  event.waitUntil(
    clients.matchAll({ type: 'window', includeUncontrolled: true })
      .then(function(clientList) {
        // Check if there's already a OneUptime window open
        for (let i = 0; i < clientList.length; i++) {
          const client = clientList[i];
          const clientUrl = new URL(client.url);
          
          if (clientUrl.origin === self.location.origin && 'focus' in client) {
            // Navigate to the target URL and focus the window
            return client.navigate(targetUrl)
              .then(() => client.focus());
          }
        }
        
        // If no OneUptime window is open, open a new one
        if (clients.openWindow) {
          return clients.openWindow(targetUrl);
        }
      })
      .catch(function(error) {
        console.error('[ServiceWorker] Error handling notification click:', error);
      })
  );
});

// Handle notification close events
self.addEventListener('notificationclose', function(event) {
  console.log('[ServiceWorker] Notification closed:', event.notification.tag);
});

// Handle background sync - removed offline functionality
self.addEventListener('sync', function(event) {
  console.log('[ServiceWorker] Background sync:', event.tag);
  // Background sync events can still be handled but no offline caching
});

// Handle messages from the main thread
self.addEventListener('message', function(event) {
  console.log('[ServiceWorker] Message received:', event.data);
  
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  } else if (event.data && event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage({ version: CACHE_VERSION });
  }
});

// Helper function to update push subscription
function updatePushSubscription(subscription) {
  return fetch('/api/push-subscription', {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(subscription)
  })
  .catch(function(error) {
    console.error('[ServiceWorker] Error updating push subscription:', error);
  });
}

console.log('[ServiceWorker] OneUptime PWA Service Worker Ready');
