/// <reference lib="webworker" />

// Configuration du service worker
const CACHE_NAME = 'veza-app-v1';
const STATIC_CACHE = 'veza-static-v1';
const DYNAMIC_CACHE = 'veza-dynamic-v1';
const API_CACHE = 'veza-api-v1';

// Ressources à mettre en cache statique
const STATIC_ASSETS = [
  '/',
  '/static/js/bundle.js',
  '/static/css/main.css',
  '/manifest.json',
  '/favicon.ico',
  '/images/logo.svg',
  '/images/offline.svg',
];

// Routes API à mettre en cache
const API_ROUTES = [
  '/api/auth/me',
  '/api/user/profile',
  '/api/conversations',
  '/api/library/tracks',
];

// Configuration des stratégies de cache
const CACHE_STRATEGIES = {
  // Cache-first pour les assets statiques
  static: {
    cacheName: STATIC_CACHE,
    strategy: 'cache-first',
    maxAge: 30 * 24 * 60 * 60 * 1000, // 30 jours
  },
  // Network-first pour les API calls
  api: {
    cacheName: API_CACHE,
    strategy: 'network-first',
    maxAge: 5 * 60 * 1000, // 5 minutes
  },
  // Stale-while-revalidate pour les images
  images: {
    cacheName: DYNAMIC_CACHE,
    strategy: 'stale-while-revalidate',
    maxAge: 7 * 24 * 60 * 60 * 1000, // 7 jours
  },
};

// Installation du service worker
self.addEventListener('install', (event: ExtendableEvent) => {
  console.log('Service Worker: Installing...');
  
  event.waitUntil(
    Promise.all([
      // Mettre en cache les assets statiques
      caches.open(STATIC_CACHE).then(cache => {
        console.log('Service Worker: Caching static assets');
        return cache.addAll(STATIC_ASSETS);
      }),
      // Mettre en cache les routes API
      caches.open(API_CACHE).then(cache => {
        console.log('Service Worker: Caching API routes');
        return cache.addAll(API_ROUTES);
      }),
    ]).then(() => {
      console.log('Service Worker: Installation complete');
      // Forcer l'activation immédiate
      return self.skipWaiting();
    })
  );
});

// Activation du service worker
self.addEventListener('activate', (event: ExtendableEvent) => {
  console.log('Service Worker: Activating...');
  
  event.waitUntil(
    Promise.all([
      // Nettoyer les anciens caches
      caches.keys().then(cacheNames => {
        return Promise.all(
          cacheNames.map(cacheName => {
            if (cacheName !== STATIC_CACHE && 
                cacheName !== DYNAMIC_CACHE && 
                cacheName !== API_CACHE) {
              console.log('Service Worker: Deleting old cache:', cacheName);
              return caches.delete(cacheName);
            }
          })
        );
      }),
      // Prendre le contrôle de tous les clients
      self.clients.claim(),
    ]).then(() => {
      console.log('Service Worker: Activation complete');
    })
  );
});

// Gestion des requêtes
self.addEventListener('fetch', (event: FetchEvent) => {
  const { request } = event;
  const url = new URL(request.url);

  // Ignorer les requêtes non-HTTP
  if (!request.url.startsWith('http')) {
    return;
  }

  // Stratégie selon le type de ressource
  if (isStaticAsset(request)) {
    event.respondWith(handleStaticAsset(request));
  } else if (isApiRequest(request)) {
    event.respondWith(handleApiRequest(request));
  } else if (isImageRequest(request)) {
    event.respondWith(handleImageRequest(request));
  } else {
    event.respondWith(handleDefaultRequest(request));
  }
});

// Vérifier si c'est un asset statique
function isStaticAsset(request: Request): boolean {
  const url = new URL(request.url);
  return (
    url.pathname.startsWith('/static/') ||
    url.pathname.endsWith('.js') ||
    url.pathname.endsWith('.css') ||
    url.pathname.endsWith('.woff2') ||
    url.pathname.endsWith('.woff') ||
    url.pathname.endsWith('.ttf') ||
    url.pathname === '/' ||
    url.pathname === '/manifest.json'
  );
}

// Vérifier si c'est une requête API
function isApiRequest(request: Request): boolean {
  const url = new URL(request.url);
  return url.pathname.startsWith('/api/');
}

// Vérifier si c'est une requête d'image
function isImageRequest(request: Request): boolean {
  const url = new URL(request.url);
  return /\.(jpg|jpeg|png|gif|webp|avif|svg)$/i.test(url.pathname);
}

// Gérer les assets statiques (cache-first)
async function handleStaticAsset(request: Request): Promise<Response> {
  const cache = await caches.open(STATIC_CACHE);
  const cachedResponse = await cache.match(request);
  
  if (cachedResponse) {
    console.log('Service Worker: Serving static asset from cache');
    return cachedResponse;
  }

  try {
    const networkResponse = await fetch(request);
    if (networkResponse.ok) {
      cache.put(request, networkResponse.clone());
    }
    return networkResponse;
  } catch (error) {
    console.error('Service Worker: Failed to fetch static asset:', error);
    return new Response('Asset not available offline', { status: 503 });
  }
}

// Gérer les requêtes API (network-first)
async function handleApiRequest(request: Request): Promise<Response> {
  const cache = await caches.open(API_CACHE);
  
  try {
    // Essayer le réseau en premier
    const networkResponse = await fetch(request);
    
    if (networkResponse.ok) {
      // Mettre en cache la réponse
      cache.put(request, networkResponse.clone());
    }
    
    return networkResponse;
  } catch (error) {
    console.log('Service Worker: Network failed, trying cache for API request');
    
    // Fallback sur le cache
    const cachedResponse = await cache.match(request);
    if (cachedResponse) {
      return cachedResponse;
    }
    
    // Retourner une réponse d'erreur appropriée
    return new Response(
      JSON.stringify({ 
        error: 'Service unavailable offline',
        message: 'Cette fonctionnalité nécessite une connexion internet'
      }),
      { 
        status: 503,
        headers: { 'Content-Type': 'application/json' }
      }
    );
  }
}

// Gérer les images (stale-while-revalidate)
async function handleImageRequest(request: Request): Promise<Response> {
  const cache = await caches.open(DYNAMIC_CACHE);
  const cachedResponse = await cache.match(request);
  
  // Mettre à jour le cache en arrière-plan
  const networkResponsePromise = fetch(request).then(response => {
    if (response.ok) {
      cache.put(request, response.clone());
    }
    return response;
  });
  
  // Retourner la version en cache si disponible, sinon attendre le réseau
  return cachedResponse || networkResponsePromise;
}

// Gérer les requêtes par défaut
async function handleDefaultRequest(request: Request): Promise<Response> {
  try {
    return await fetch(request);
  } catch (error) {
    console.error('Service Worker: Failed to fetch request:', error);
    
    // Pour les requêtes de navigation, retourner la page offline
    if (request.mode === 'navigate') {
      return caches.match('/offline.html') || new Response(
        '<html><body><h1>Hors ligne</h1><p>Cette page n\'est pas disponible hors ligne.</p></body></html>',
        { headers: { 'Content-Type': 'text/html' } }
      );
    }
    
    return new Response('Resource not available offline', { status: 503 });
  }
}

// Gestion des messages du service worker
self.addEventListener('message', (event: MessageEvent) => {
  const { type, payload } = event.data;
  
  switch (type) {
    case 'SKIP_WAITING':
      self.skipWaiting();
      break;
      
    case 'CACHE_URLS':
      event.waitUntil(cacheUrls(payload.urls));
      break;
      
    case 'CLEAR_CACHE':
      event.waitUntil(clearCache(payload.cacheName));
      break;
      
    case 'GET_CACHE_SIZE':
      event.waitUntil(getCacheSize().then(size => {
        event.ports[0]?.postMessage({ type: 'CACHE_SIZE', size });
      }));
      break;
      
    default:
      console.log('Service Worker: Unknown message type:', type);
  }
});

// Mettre en cache des URLs spécifiques
async function cacheUrls(urls: string[]): Promise<void> {
  const cache = await caches.open(DYNAMIC_CACHE);
  await cache.addAll(urls);
  console.log('Service Worker: Cached URLs:', urls);
}

// Nettoyer un cache spécifique
async function clearCache(cacheName: string): Promise<void> {
  const deleted = await caches.delete(cacheName);
  console.log('Service Worker: Cache cleared:', cacheName, deleted);
}

// Obtenir la taille du cache
async function getCacheSize(): Promise<number> {
  const cacheNames = await caches.keys();
  let totalSize = 0;
  
  for (const cacheName of cacheNames) {
    const cache = await caches.open(cacheName);
    const keys = await cache.keys();
    totalSize += keys.length;
  }
  
  return totalSize;
}

// Gestion des notifications push
self.addEventListener('push', (event: PushEvent) => {
  console.log('Service Worker: Push received');
  
  const options: NotificationOptions = {
    body: event.data?.text() || 'Nouvelle notification Veza',
    icon: '/images/icon-192x192.png',
    badge: '/images/badge-72x72.png',
    tag: 'veza-notification',
    requireInteraction: true,
    actions: [
      {
        action: 'open',
        title: 'Ouvrir',
        icon: '/images/open-icon.png'
      },
      {
        action: 'close',
        title: 'Fermer',
        icon: '/images/close-icon.png'
      }
    ]
  };
  
  event.waitUntil(
    self.registration.showNotification('Veza', options)
  );
});

// Gestion des clics sur les notifications
self.addEventListener('notificationclick', (event: NotificationEvent) => {
  console.log('Service Worker: Notification clicked');
  
  event.notification.close();
  
  if (event.action === 'open' || !event.action) {
    event.waitUntil(
      self.clients.openWindow('/')
    );
  }
});

// Gestion de la synchronisation en arrière-plan
self.addEventListener('sync', (event: SyncEvent) => {
  console.log('Service Worker: Background sync triggered');
  
  if (event.tag === 'background-sync') {
    event.waitUntil(doBackgroundSync());
  }
});

// Effectuer la synchronisation en arrière-plan
async function doBackgroundSync(): Promise<void> {
  try {
    // Synchroniser les messages en attente
    const pendingMessages = await getPendingMessages();
    for (const message of pendingMessages) {
      await syncMessage(message);
    }
    
    // Synchroniser les uploads en attente
    const pendingUploads = await getPendingUploads();
    for (const upload of pendingUploads) {
      await syncUpload(upload);
    }
    
    console.log('Service Worker: Background sync completed');
  } catch (error) {
    console.error('Service Worker: Background sync failed:', error);
  }
}

// Obtenir les messages en attente (à implémenter avec IndexedDB)
async function getPendingMessages(): Promise<any[]> {
  // TODO: Implémenter avec IndexedDB
  return [];
}

// Obtenir les uploads en attente (à implémenter avec IndexedDB)
async function getPendingUploads(): Promise<any[]> {
  // TODO: Implémenter avec IndexedDB
  return [];
}

// Synchroniser un message
async function syncMessage(message: any): Promise<void> {
  // TODO: Implémenter la synchronisation des messages
  console.log('Service Worker: Syncing message:', message);
}

// Synchroniser un upload
async function syncUpload(upload: any): Promise<void> {
  // TODO: Implémenter la synchronisation des uploads
  console.log('Service Worker: Syncing upload:', upload);
}
