/// <reference types="@sveltejs/kit" />
/// <reference no-default-lib="true"/>
/// <reference lib="esnext" />
/// <reference lib="webworker" />

import { build, files, version } from '$service-worker';

declare let self: ServiceWorkerGlobalScope;

// Create a unique cache name for this deployment  
const CACHE = `cache-${version}`;
const ASSETS = [
  ...build, // the app itself
  ...files  // everything in `static`
];

// Global error handlers (preserve existing)
self.addEventListener('error', (event) => {
  console.error('[SW] Global error:', event.error);
  console.error('[SW] Error details:', {
    message: event.message,
    filename: event.filename,
    lineno: event.lineno,
    colno: event.colno,
    error: event.error
  });
});

self.addEventListener('unhandledrejection', (event) => {
  console.error('[SW] Unhandled promise rejection:', event.reason);
  event.preventDefault(); // Prevent default browser behavior
});

console.log('[SW] Service worker script loading...');

// Install event - cache all assets
self.addEventListener('install', (event) => {
  console.log('[SW] Installing service worker...');
  
  async function addFilesToCache() {
    const cache = await caches.open(CACHE);
    await cache.addAll(ASSETS);
    console.log(`[SW] Cached ${ASSETS.length} assets`);
  }

  event.waitUntil(addFilesToCache());
});

// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
  console.log('[SW] Activating service worker...');
  
  async function deleteOldCaches() {
    for (const key of await caches.keys()) {
      if (key !== CACHE) {
        console.log('[SW] Deleting old cache:', key);
        await caches.delete(key);
      }
    }
  }

  event.waitUntil(deleteOldCaches());
});

// Fetch event - serve from cache with network fallback
self.addEventListener('fetch', (event) => {
  // ignore POST requests etc
  if (event.request.method !== 'GET') return;

  async function respond() {
    const url = new URL(event.request.url);
    const cache = await caches.open(CACHE);

    // `build`/`files` can always be served from the cache
    if (ASSETS.includes(url.pathname)) {
      const response = await cache.match(url.pathname);
      if (response) {
        return response;
      }
    }

    // for everything else, try the network first, but
    // fall back to the cache if we're offline
    try {
      const response = await fetch(event.request);
      
      // if we're offline, fetch can return a value that is not a Response
      // instead of throwing - and we can't pass this non-Response to respondWith
      if (!(response instanceof Response)) {
        throw new Error('invalid response from fetch');
      }

      if (response.status === 200) {
        cache.put(event.request, response.clone());
      }
      return response;
    } catch (err) {
      const response = await cache.match(event.request);
      if (response) {
        return response;
      }
      
      // if there's no cache, then just error out
      // as there is nothing we can do to respond to this request
      throw err;
    }
  }

  event.respondWith(respond());
});

// Push notification handling
self.addEventListener('push', (event) => {
  console.log('[SW] Push event received:', event);
  
  if (!event.data) {
    console.log('[SW] Push event but no data');
    return;
  }

  let data;
  try {
    data = event.data.json();
  } catch (e) {
    console.error('[SW] Failed to parse push data:', e);
    return;
  }

  console.log('[SW] Push data:', data);

  const options: NotificationOptions = {
    body: data.body || 'Recipe processing update',
    icon: '/favicon.png',
    badge: '/favicon.png',
    data: data,
    requireInteraction: data.requireInteraction || false,
    silent: false,
    tag: data.tag || 'recipe-update',
    timestamp: Date.now(),
    actions: []
  };

  // Add actions based on notification type
  if (data.type === 'success' && data.itemId) {
    options.actions = [
      {
        action: 'view',
        title: 'View Recipe',
        icon: '/favicon.png'
      },
      {
        action: 'dismiss',
        title: 'Dismiss'
      }
    ];
  } else if (data.type === 'error' && data.itemId) {
    options.actions = [
      {
        action: 'retry',
        title: 'Retry',
        icon: '/favicon.png'
      },
      {
        action: 'view',
        title: 'View Details'
      }
    ];
  }

  const title = data.title || getNotificationTitle(data.type, data);

  event.waitUntil(
    self.registration.showNotification(title, options)
  );
});

// Handle notification clicks
self.addEventListener('notificationclick', (event) => {
  console.log('[SW] Notification click received:', event);
  
  event.notification.close();

  const data = event.notification.data;
  const action = event.action;

  let url = '/';

  if (action === 'view' && data?.itemId) {
    url = `/?highlight=${data.itemId}`;
  } else if (action === 'retry' && data?.itemId) {
    // Navigate to dashboard and trigger retry via postMessage
    url = `/?highlight=${data.itemId}&action=retry`;
  } else if (data?.itemId) {
    url = `/?highlight=${data.itemId}`;
  }

  event.waitUntil(
    clients.matchAll({ type: 'window', includeUncontrolled: true })
      .then((clientsList) => {
        // Check if there's already a window/tab open
        for (const client of clientsList) {
          if (client.url.includes(self.location.origin) && 'focus' in client) {
            return client.focus().then(() => {
              // Send message to the client about the action
              return client.postMessage({
                type: 'notification-action',
                action: action,
                data: data
              });
            });
          }
        }
        
        // If no window is open, open a new one
        if (clients.openWindow) {
          return clients.openWindow(url);
        }
      })
  );
});

// Handle notification close
self.addEventListener('notificationclose', (event) => {
  console.log('[SW] Notification closed:', event);
  
  // Track notification dismissals if needed
  const data = event.notification.data;
  if (data?.analytics) {
    // Could send analytics event here
    console.log('[SW] Notification dismissed:', data);
  }
});

// Background sync for retry operations
self.addEventListener('sync', (event) => {
  console.log('[SW] Background sync:', event.tag);
  
  if (event.tag === 'retry-queue-item') {
    event.waitUntil(handleRetrySync());
  }
});

// Helper functions
function getNotificationTitle(type: string, data: any): string {
  switch (type) {
    case 'success':
      return data.recipeName 
        ? `✅ Recipe Ready: ${data.recipeName}`
        : '✅ Recipe extraction complete';
    case 'error':
      return '❌ Recipe extraction failed';
    case 'progress':
      return `🔄 Processing recipe...`;
    default:
      return '📱 InstaRecipe Update';
  }
}

async function handleRetrySync() {
  try {
    // Get retry items from IndexedDB or localStorage if needed
    console.log('[SW] Handling retry sync');
    
    // This could implement background retry logic
    // For now, we'll let the main app handle retries
    return Promise.resolve();
  } catch (error) {
    console.error('[SW] Retry sync failed:', error);
    throw error;
  }
}

// Message handling for communication with main app
self.addEventListener('message', (event) => {
  console.log('[SW] Message received:', event.data);

  const { type, data } = event.data;

  switch (type) {
    case 'SKIP_WAITING':
      self.skipWaiting();
      break;
    case 'GET_VERSION':
      event.ports[0].postMessage({ version: '1.0.0' });
      break;
    case 'QUEUE_RETRY':
      // Queue a background sync for retry
      self.registration.sync.register('retry-queue-item');
      break;
    default:
      console.log('[SW] Unknown message type:', type);
  }
});