/**
 * TASKMATE - Service Worker
 * ==========================
 * Offline support and caching
 */

const CACHE_VERSION = '405';
const CACHE_NAME = 'taskmate-v' + CACHE_VERSION;
const STATIC_CACHE_NAME = 'taskmate-static-v' + CACHE_VERSION;
const DYNAMIC_CACHE_NAME = 'taskmate-dynamic-v' + CACHE_VERSION;

// Files to cache immediately
const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/css/variables.css',
  '/css/base.css',
  '/css/components.css',
  '/css/board.css',
  '/css/modal.css',
  '/css/calendar.css',
  '/css/responsive.css',
  '/js/app.js',
  '/js/utils.js',
  '/js/api.js',
  '/js/auth.js',
  '/js/store.js',
  '/js/sync.js',
  '/js/offline.js',
  '/js/board.js',
  '/js/task-modal.js',
  '/js/calendar.js',
  '/js/list.js',
  '/js/shortcuts.js',
  '/js/undo.js',
  '/js/admin.js',
  '/js/proposals.js',
  '/js/notifications.js',
  '/js/gitea.js',
  '/js/knowledge.js',
  '/js/coding.js',
  '/js/mobile.js',
  '/js/reminders.js',
  '/js/contacts.js',
  '/js/pwa.js',
  '/css/list.css',
  '/css/mobile.css',
  '/css/admin.css',
  '/css/proposals.css',
  '/css/notifications.css',
  '/css/gitea.css',
  '/css/knowledge.css',
  '/css/coding.css',
  '/css/reminders.css',
  '/css/contacts.css',
  '/css/pwa.css',
  '/manifest.json'
];

// API routes to cache
const API_CACHE_ROUTES = [
  '/api/projects',
  '/api/auth/users',
  '/api/columns',
  '/api/tasks',
  '/api/labels',
  '/api/proposals',
  '/api/knowledge',
  '/api/reminders',
  '/api/contacts'
];

// Assets to skip caching
const SKIP_CACHE_PATTERNS = [
  /\/api\/auth\/login/,
  /\/api\/auth\/logout/,
  /\/api\/files\//,
  /\/uploads\//,
  /socket\.io/
];

// Install event - cache static assets
self.addEventListener('install', (event) => {
  console.log('[SW] Installing...');

  event.waitUntil(
    caches.open(STATIC_CACHE_NAME)
      .then((cache) => {
        console.log('[SW] Caching static assets');
        return cache.addAll(STATIC_ASSETS);
      })
      .then(() => self.skipWaiting())
  );
});

// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
  console.log('[SW] Activating...');

  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
          .filter((name) => {
            return name.startsWith('taskmate-') &&
                   name !== STATIC_CACHE_NAME &&
                   name !== DYNAMIC_CACHE_NAME;
          })
          .map((name) => {
            console.log('[SW] Deleting old cache:', name);
            return caches.delete(name);
          })
      );
    }).then(() => self.clients.claim())
  );
});

// Fetch event - serve from cache or network
self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);

  // Skip non-GET requests
  if (event.request.method !== 'GET') {
    return;
  }

  // Skip WebSocket requests
  if (url.protocol === 'ws:' || url.protocol === 'wss:') {
    return;
  }

  // Handle API requests
  if (url.pathname.startsWith('/api/')) {
    event.respondWith(handleApiRequest(event.request));
    return;
  }

  // Handle static assets
  event.respondWith(handleStaticRequest(event.request));
});

// Handle static asset requests - Cache First for assets, Network First for HTML
async function handleStaticRequest(request) {
  const url = new URL(request.url);
  
  // Check if should skip caching
  if (SKIP_CACHE_PATTERNS.some(pattern => pattern.test(url.pathname))) {
    return fetch(request);
  }
  
  // For HTML files, use Network First
  if (request.mode === 'navigate' || url.pathname.endsWith('.html') || url.pathname === '/') {
    try {
      const networkResponse = await fetch(request);
      
      if (networkResponse.ok) {
        const cache = await caches.open(STATIC_CACHE_NAME);
        cache.put(request, networkResponse.clone());
      }
      
      return networkResponse;
    } catch (error) {
      const cachedResponse = await caches.match(request);
      if (cachedResponse) {
        console.log('[SW] Serving HTML from cache (offline):', request.url);
        return cachedResponse;
      }
      
      // Return offline page
      const offlinePage = await caches.match('/index.html');
      if (offlinePage) {
        return offlinePage;
      }
      
      throw error;
    }
  }
  
  // For static assets (CSS, JS, images), use Cache First
  const cachedResponse = await caches.match(request);
  if (cachedResponse) {
    // Update cache in background
    fetchAndCache(request);
    return cachedResponse;
  }
  
  // If not in cache, fetch from network
  try {
    const networkResponse = await fetch(request);
    
    if (networkResponse.ok) {
      const cache = await caches.open(STATIC_CACHE_NAME);
      cache.put(request, networkResponse.clone());
    }
    
    return networkResponse;
  } catch (error) {
    console.error('[SW] Network failed for:', request.url);
    throw error;
  }
}

// Background cache update
async function fetchAndCache(request) {
  try {
    const networkResponse = await fetch(request);
    if (networkResponse.ok) {
      const cache = await caches.open(STATIC_CACHE_NAME);
      cache.put(request, networkResponse);
    }
  } catch (error) {
    // Silently fail - we already served from cache
  }
}

// Handle API requests
async function handleApiRequest(request) {
  const url = new URL(request.url);
  
  // Check if should skip caching
  if (SKIP_CACHE_PATTERNS.some(pattern => pattern.test(url.pathname))) {
    return fetch(request);
  }

  // Check if this is a cacheable API route
  const isCacheable = API_CACHE_ROUTES.some(route =>
    url.pathname.startsWith(route)
  );

  // Try network first for API requests
  try {
    const networkResponse = await fetch(request);

    // Cache successful GET responses for cacheable routes
    if (networkResponse.ok && isCacheable) {
      const cache = await caches.open(DYNAMIC_CACHE_NAME);
      cache.put(request, networkResponse.clone());
    }

    return networkResponse;
  } catch (error) {
    // If offline, try to return cached response
    if (isCacheable) {
      const cachedResponse = await caches.match(request);
      if (cachedResponse) {
        console.log('[SW] Serving cached API response:', request.url);
        // Add offline header to indicate cached response
        const headers = new Headers(cachedResponse.headers);
        headers.set('X-From-Cache', 'true');
        
        return new Response(cachedResponse.body, {
          status: cachedResponse.status,
          statusText: cachedResponse.statusText,
          headers: headers
        });
      }
    }

    // Return error response
    return new Response(
      JSON.stringify({
        success: false,
        error: 'Keine Internetverbindung',
        offline: true
      }),
      {
        status: 503,
        headers: { 'Content-Type': 'application/json' }
      }
    );
  }
}

// Background sync
self.addEventListener('sync', (event) => {
  console.log('[SW] Background sync:', event.tag);

  if (event.tag === 'sync-pending') {
    event.waitUntil(syncPendingOperations());
  }
});

// Sync pending operations
async function syncPendingOperations() {
  // This will be handled by the main app
  // Just notify clients that sync is needed
  const clients = await self.clients.matchAll();
  clients.forEach(client => {
    client.postMessage({
      type: 'SYNC_NEEDED'
    });
  });
}

// Push notifications (for future use)
self.addEventListener('push', (event) => {
  if (!event.data) return;

  const data = event.data.json();

  const options = {
    body: data.body,
    icon: '/icons/icon-192.png',
    badge: '/icons/badge-72.png',
    vibrate: [100, 50, 100],
    data: {
      url: data.url || '/'
    },
    actions: [
      { action: 'open', title: 'Öffnen' },
      { action: 'close', title: 'Schließen' }
    ]
  };

  event.waitUntil(
    self.registration.showNotification(data.title || 'TaskMate', options)
  );
});

// Notification click
self.addEventListener('notificationclick', (event) => {
  event.notification.close();

  if (event.action === 'close') {
    return;
  }

  const url = event.notification.data?.url || '/';

  event.waitUntil(
    self.clients.matchAll({ type: 'window' }).then((clients) => {
      // Check if there's already a window open
      for (const client of clients) {
        if (client.url === url && 'focus' in client) {
          return client.focus();
        }
      }

      // Open new window if none found
      if (self.clients.openWindow) {
        return self.clients.openWindow(url);
      }
    })
  );
});

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

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

  if (event.data.type === 'CACHE_URLS') {
    event.waitUntil(
      caches.open(DYNAMIC_CACHE_NAME).then((cache) => {
        return cache.addAll(event.data.urls);
      })
    );
  }

  if (event.data.type === 'CLEAR_CACHE') {
    event.waitUntil(
      caches.keys().then((cacheNames) => {
        return Promise.all(
          cacheNames.map((name) => caches.delete(name))
        );
      })
    );
  }
});

console.log('[SW] Service Worker loaded');
