// Advanced Service Worker for SIF-GT PWA
const VERSION = '1.0.0';
const CACHE_PREFIX = 'sif-gt';
const STATIC_CACHE = `${CACHE_PREFIX}-static-v${VERSION}`;
const DYNAMIC_CACHE = `${CACHE_PREFIX}-dynamic-v${VERSION}`;
const API_CACHE = `${CACHE_PREFIX}-api-v${VERSION}`;
const IMAGE_CACHE = `${CACHE_PREFIX}-images-v${VERSION}`;

// Cache configuration
const CACHE_CONFIG = {
  static: {
    name: STATIC_CACHE,
    maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
    maxEntries: 100
  },
  dynamic: {
    name: DYNAMIC_CACHE,
    maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
    maxEntries: 50
  },
  api: {
    name: API_CACHE,
    maxAge: 24 * 60 * 60 * 1000, // 1 day
    maxEntries: 200
  },
  images: {
    name: IMAGE_CACHE,
    maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
    maxEntries: 100
  }
};

// Resources to cache immediately
const STATIC_ASSETS = [
  '/',
  '/static/js/bundle.js',
  '/static/css/main.css',
  '/manifest.json',
  '/favicon.ico',
  '/logo192.png',
  '/logo512.png'
];

// API endpoints that should be cached for offline access
const CACHEABLE_API_ENDPOINTS = [
  '/api/species',
  '/api/projects',
  '/api/rodales',
  '/api/auth/me'
];

// Sync tags for background sync
const SYNC_TAGS = {
  FIELD_DATA: 'sync-field-data',
  PROJECTS: 'sync-projects',
  RODALES: 'sync-rodales'
};

// Install event - cache static assets and handle updates
self.addEventListener('install', event => {
  console.log(`Service Worker v${VERSION} installing...`);
  
  event.waitUntil(
    Promise.all([
      // Cache static assets
      caches.open(STATIC_CACHE).then(cache => {
        console.log('Caching static assets');
        return cache.addAll(STATIC_ASSETS);
      }),
      
      // Pre-cache essential API endpoints
      cacheEssentialApiEndpoints()
    ])
    .then(() => {
      console.log('All caches initialized successfully');
      
      // Notify clients about new version available
      return notifyClientsAboutUpdate();
    })
    .then(() => {
      // Skip waiting to activate immediately (for updates)
      return self.skipWaiting();
    })
    .catch(error => {
      console.error('Failed to install service worker:', error);
    })
  );
});

// Pre-cache essential API endpoints for offline functionality
async function cacheEssentialApiEndpoints() {
  try {
    const apiCache = await caches.open(API_CACHE);
    
    // Only cache GET requests that are likely to be needed offline
    const cachePromises = CACHEABLE_API_ENDPOINTS.map(async endpoint => {
      try {
        const response = await fetch(endpoint, {
          headers: {
            'Accept': 'application/json'
          }
        });
        
        if (response.ok) {
          await apiCache.put(endpoint, response.clone());
          console.log(`Cached API endpoint: ${endpoint}`);
        }
      } catch (error) {
        console.log(`Could not pre-cache ${endpoint}:`, error.message);
      }
    });
    
    await Promise.allSettled(cachePromises);
  } catch (error) {
    console.error('Failed to cache essential API endpoints:', error);
  }
}

// Notify clients about service worker update
async function notifyClientsAboutUpdate() {
  const clients = await self.clients.matchAll();
  
  clients.forEach(client => {
    client.postMessage({
      type: 'SW_UPDATE_AVAILABLE',
      version: VERSION,
      message: 'Nueva versión de la aplicación disponible'
    });
  });
}

// Activate event - clean up old caches and manage cache limits
self.addEventListener('activate', event => {
  console.log(`Service Worker v${VERSION} activating...`);
  
  event.waitUntil(
    Promise.all([
      // Clean up old caches
      cleanupOldCaches(),
      
      // Enforce cache limits
      enforceCacheLimits(),
      
      // Clean expired cache entries
      cleanExpiredCacheEntries()
    ])
    .then(() => {
      console.log('Service Worker activated and caches cleaned');
      
      // Notify clients about activation
      return notifyClientsAboutActivation();
    })
    .then(() => {
      return self.clients.claim();
    })
    .catch(error => {
      console.error('Failed to activate service worker:', error);
    })
  );
});

// Clean up old cache versions
async function cleanupOldCaches() {
  const cacheNames = await caches.keys();
  const currentCaches = [STATIC_CACHE, DYNAMIC_CACHE, API_CACHE, IMAGE_CACHE];
  
  const deletePromises = cacheNames
    .filter(cacheName => {
      return cacheName.startsWith(CACHE_PREFIX) && !currentCaches.includes(cacheName);
    })
    .map(cacheName => {
      console.log('Deleting old cache:', cacheName);
      return caches.delete(cacheName);
    });
  
  return Promise.all(deletePromises);
}

// Enforce cache size limits
async function enforceCacheLimits() {
  const cacheConfigs = Object.values(CACHE_CONFIG);
  
  const limitPromises = cacheConfigs.map(async config => {
    try {
      const cache = await caches.open(config.name);
      const keys = await cache.keys();
      
      if (keys.length > config.maxEntries) {
        const entriesToDelete = keys.length - config.maxEntries;
        const keysToDelete = keys.slice(0, entriesToDelete);
        
        console.log(`Removing ${entriesToDelete} entries from ${config.name}`);
        
        const deletePromises = keysToDelete.map(key => cache.delete(key));
        await Promise.all(deletePromises);
      }
    } catch (error) {
      console.error(`Failed to enforce limits for ${config.name}:`, error);
    }
  });
  
  return Promise.all(limitPromises);
}

// Clean expired cache entries
async function cleanExpiredCacheEntries() {
  const now = Date.now();
  
  const cleanupPromises = Object.values(CACHE_CONFIG).map(async config => {
    try {
      const cache = await caches.open(config.name);
      const keys = await cache.keys();
      
      const deletePromises = keys.map(async request => {
        const response = await cache.match(request);
        if (response) {
          const cachedTime = response.headers.get('sw-cached-time');
          if (cachedTime && (now - parseInt(cachedTime)) > config.maxAge) {
            console.log(`Removing expired entry from ${config.name}:`, request.url);
            return cache.delete(request);
          }
        }
      });
      
      await Promise.all(deletePromises);
    } catch (error) {
      console.error(`Failed to clean expired entries from ${config.name}:`, error);
    }
  });
  
  return Promise.all(cleanupPromises);
}

// Notify clients about service worker activation
async function notifyClientsAboutActivation() {
  const clients = await self.clients.matchAll();
  
  clients.forEach(client => {
    client.postMessage({
      type: 'SW_ACTIVATED',
      version: VERSION,
      message: 'Service Worker activado correctamente'
    });
  });
}

// Fetch event - implement advanced caching strategies
self.addEventListener('fetch', event => {
  const { request } = event;
  const url = new URL(request.url);

  // Skip non-GET requests for caching (except for offline fallbacks)
  if (request.method !== 'GET' && !url.pathname.startsWith('/api/')) {
    return;
  }

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

  // Handle images with Stale While Revalidate strategy
  if (request.destination === 'image') {
    event.respondWith(handleImageRequest(request));
    return;
  }

  // Handle static assets (JS, CSS) with Cache First strategy
  if (request.destination === 'script' || 
      request.destination === 'style' ||
      request.destination === 'font' ||
      request.destination === 'manifest') {
    event.respondWith(handleStaticAssets(request));
    return;
  }

  // Handle navigation requests with Network First, Cache Fallback
  if (request.mode === 'navigate') {
    event.respondWith(handleNavigation(request));
    return;
  }

  // Handle documents and other resources with Cache First
  event.respondWith(handleDocumentRequest(request));
});

// Handle API requests - Network First with enhanced caching
async function handleApiRequest(request) {
  const url = new URL(request.url);
  
  try {
    // Try network first
    const networkResponse = await fetch(request.clone());
    
    // Cache successful GET requests with timestamp
    if (request.method === 'GET' && networkResponse.ok) {
      const responseToCache = networkResponse.clone();
      
      // Add timestamp header for cache expiration
      const headers = new Headers(responseToCache.headers);
      headers.set('sw-cached-time', Date.now().toString());
      
      const cachedResponse = new Response(responseToCache.body, {
        status: responseToCache.status,
        statusText: responseToCache.statusText,
        headers: headers
      });
      
      const cache = await caches.open(API_CACHE);
      await cache.put(request, cachedResponse);
      
      console.log(`Cached API response: ${request.url}`);
    }
    
    return networkResponse;
  } catch (error) {
    console.log('Network failed, trying cache for:', request.url);
    
    // Try to get from cache
    const cachedResponse = await caches.match(request);
    
    if (cachedResponse) {
      // Add offline indicator header
      const headers = new Headers(cachedResponse.headers);
      headers.set('x-served-from-cache', 'true');
      
      return new Response(cachedResponse.body, {
        status: cachedResponse.status,
        statusText: cachedResponse.statusText,
        headers: headers
      });
    }
    
    // Handle POST/PUT/DELETE requests when offline
    if (request.method !== 'GET') {
      return handleOfflineApiRequest(request);
    }
    
    // Return offline response for GET requests
    return new Response(
      JSON.stringify({ 
        error: 'Offline', 
        message: 'No hay conexión disponible',
        offline: true
      }),
      {
        status: 503,
        headers: { 
          'Content-Type': 'application/json',
          'x-offline-response': 'true'
        }
      }
    );
  }
}

// Handle offline API requests (POST/PUT/DELETE)
async function handleOfflineApiRequest(request) {
  const url = new URL(request.url);
  
  // Store request for background sync
  if (url.pathname.includes('/field-data') || url.pathname.includes('/sync')) {
    await storeRequestForSync(request);
    
    return new Response(
      JSON.stringify({
        success: true,
        message: 'Datos guardados localmente. Se sincronizarán cuando haya conexión.',
        queued: true
      }),
      {
        status: 202,
        headers: { 'Content-Type': 'application/json' }
      }
    );
  }
  
  // For other requests, return error
  return new Response(
    JSON.stringify({
      error: 'Offline',
      message: 'Esta operación requiere conexión a internet'
    }),
    {
      status: 503,
      headers: { 'Content-Type': 'application/json' }
    }
  );
}

// Handle static assets - Cache First strategy
async function handleStaticAssets(request) {
  const cachedResponse = await caches.match(request);
  
  if (cachedResponse) {
    return cachedResponse;
  }
  
  try {
    const response = await fetch(request);
    
    if (response.ok) {
      // Add timestamp for cache expiration
      const headers = new Headers(response.headers);
      headers.set('sw-cached-time', Date.now().toString());
      
      const responseToCache = new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers: headers
      });
      
      const cache = await caches.open(STATIC_CACHE);
      await cache.put(request, responseToCache);
      
      return response;
    }
    
    return response;
  } catch (error) {
    console.error('Failed to fetch static asset:', request.url);
    throw error;
  }
}

// Handle images - Stale While Revalidate strategy
async function handleImageRequest(request) {
  const cachedResponse = await caches.match(request);
  
  // Return cached version immediately if available
  if (cachedResponse) {
    // Update cache in background
    fetch(request).then(response => {
      if (response.ok) {
        const headers = new Headers(response.headers);
        headers.set('sw-cached-time', Date.now().toString());
        
        const responseToCache = new Response(response.body, {
          status: response.status,
          statusText: response.statusText,
          headers: headers
        });
        
        caches.open(IMAGE_CACHE).then(cache => {
          cache.put(request, responseToCache);
        });
      }
    }).catch(error => {
      console.log('Background image update failed:', error);
    });
    
    return cachedResponse;
  }
  
  // No cached version, fetch from network
  try {
    const response = await fetch(request);
    
    if (response.ok) {
      const headers = new Headers(response.headers);
      headers.set('sw-cached-time', Date.now().toString());
      
      const responseToCache = new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers: headers
      });
      
      const cache = await caches.open(IMAGE_CACHE);
      await cache.put(request, responseToCache);
      
      return response;
    }
    
    return response;
  } catch (error) {
    console.error('Failed to fetch image:', request.url);
    throw error;
  }
}

// Handle document requests - Cache First strategy
async function handleDocumentRequest(request) {
  const cachedResponse = await caches.match(request);
  
  if (cachedResponse) {
    return cachedResponse;
  }
  
  try {
    const response = await fetch(request);
    
    if (response.ok) {
      const headers = new Headers(response.headers);
      headers.set('sw-cached-time', Date.now().toString());
      
      const responseToCache = new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers: headers
      });
      
      const cache = await caches.open(DYNAMIC_CACHE);
      await cache.put(request, responseToCache);
    }
    
    return response;
  } catch (error) {
    console.error('Failed to fetch document:', request.url);
    
    // Try to serve cached index.html for navigation requests
    if (request.mode === 'navigate') {
      const indexResponse = await caches.match('/');
      return indexResponse || new Response('Offline', { status: 503 });
    }
    
    throw error;
  }
}

// Handle navigation - Network First with cache fallback
async function handleNavigation(request) {
  try {
    // Try network first for navigation
    const response = await fetch(request);
    
    if (response.ok) {
      // Cache successful navigation responses
      const headers = new Headers(response.headers);
      headers.set('sw-cached-time', Date.now().toString());
      
      const responseToCache = new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers: headers
      });
      
      const cache = await caches.open(DYNAMIC_CACHE);
      await cache.put(request, responseToCache);
    }
    
    return response;
  } catch (error) {
    console.log('Network failed for navigation, serving cached content');
    
    // Try to serve cached version of the same route
    const cachedResponse = await caches.match(request);
    if (cachedResponse) {
      return cachedResponse;
    }
    
    // Fallback to cached index.html for SPA routing
    const indexResponse = await caches.match('/');
    if (indexResponse) {
      return indexResponse;
    }
    
    // Last resort: offline page
    return new Response(`
      <!DOCTYPE html>
      <html>
        <head>
          <title>SIF-GT - Offline</title>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <style>
            body {
              font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
              display: flex;
              flex-direction: column;
              align-items: center;
              justify-content: center;
              height: 100vh;
              margin: 0;
              background: #f5f5f5;
              color: #333;
            }
            .offline-container {
              text-align: center;
              max-width: 400px;
              padding: 20px;
            }
            .offline-icon {
              font-size: 64px;
              margin-bottom: 20px;
            }
            h1 {
              color: #2196F3;
              margin-bottom: 16px;
            }
            p {
              line-height: 1.5;
              margin-bottom: 20px;
            }
            button {
              background: #2196F3;
              color: white;
              border: none;
              padding: 12px 24px;
              border-radius: 6px;
              cursor: pointer;
              font-size: 16px;
            }
            button:hover {
              background: #1976D2;
            }
          </style>
        </head>
        <body>
          <div class="offline-container">
            <div class="offline-icon">🌲</div>
            <h1>SIF-GT</h1>
            <p>No hay conexión a internet disponible.</p>
            <p>Algunas funciones pueden estar limitadas mientras esté offline.</p>
            <button onclick="window.location.reload()">Reintentar</button>
          </div>
        </body>
      </html>
    `, {
      status: 503,
      headers: {
        'Content-Type': 'text/html',
        'Cache-Control': 'no-cache'
      }
    });
  }
}

// Background sync event handler
self.addEventListener('sync', event => {
  console.log('Background sync triggered:', event.tag);
  
  switch (event.tag) {
    case SYNC_TAGS.FIELD_DATA:
      event.waitUntil(syncFieldData());
      break;
    case SYNC_TAGS.PROJECTS:
      event.waitUntil(syncProjects());
      break;
    case SYNC_TAGS.RODALES:
      event.waitUntil(syncRodales());
      break;
    default:
      console.log('Unknown sync tag:', event.tag);
  }
});

// Store request for background sync
async function storeRequestForSync(request) {
  try {
    const requestData = {
      url: request.url,
      method: request.method,
      headers: Object.fromEntries(request.headers.entries()),
      body: request.method !== 'GET' ? await request.text() : null,
      timestamp: Date.now()
    };
    
    // Store in IndexedDB for background sync
    const db = await openSyncDB();
    const transaction = db.transaction(['sync_queue'], 'readwrite');
    const store = transaction.objectStore('sync_queue');
    
    await store.add(requestData);
    
    // Register background sync
    await self.registration.sync.register(SYNC_TAGS.FIELD_DATA);
    
    console.log('Request stored for background sync:', request.url);
  } catch (error) {
    console.error('Failed to store request for sync:', error);
  }
}

// Open IndexedDB for sync queue
function openSyncDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open('SIF_GT_SYNC', 1);
    
    request.onerror = () => reject(request.error);
    request.onsuccess = () => resolve(request.result);
    
    request.onupgradeneeded = event => {
      const db = event.target.result;
      
      if (!db.objectStoreNames.contains('sync_queue')) {
        const store = db.createObjectStore('sync_queue', { 
          keyPath: 'id', 
          autoIncrement: true 
        });
        store.createIndex('timestamp', 'timestamp');
        store.createIndex('url', 'url');
      }
    };
  });
}

// Sync field data when connection is restored
async function syncFieldData() {
  try {
    console.log('Starting field data synchronization...');
    
    const db = await openSyncDB();
    const transaction = db.transaction(['sync_queue'], 'readwrite');
    const store = transaction.objectStore('sync_queue');
    
    const requests = await store.getAll();
    const fieldDataRequests = requests.filter(req => 
      req.url.includes('/field-data') || req.url.includes('/sync')
    );
    
    if (fieldDataRequests.length === 0) {
      console.log('No field data to sync');
      return;
    }
    
    let syncedCount = 0;
    let failedCount = 0;
    
    for (const requestData of fieldDataRequests) {
      try {
        const response = await fetch(requestData.url, {
          method: requestData.method,
          headers: requestData.headers,
          body: requestData.body
        });
        
        if (response.ok) {
          // Remove from sync queue
          await store.delete(requestData.id);
          syncedCount++;
          console.log('Synced request:', requestData.url);
        } else {
          failedCount++;
          console.error('Sync failed for:', requestData.url, response.status);
        }
      } catch (error) {
        failedCount++;
        console.error('Network error during sync:', requestData.url, error);
      }
    }
    
    // Notify clients about sync completion
    await notifyClientsAboutSync({
      type: 'FIELD_DATA_SYNC_COMPLETE',
      synced: syncedCount,
      failed: failedCount,
      total: fieldDataRequests.length
    });
    
    // Show notification if sync was successful
    if (syncedCount > 0) {
      await showSyncNotification(syncedCount, failedCount);
    }
    
  } catch (error) {
    console.error('Failed to sync field data:', error);
    
    await notifyClientsAboutSync({
      type: 'FIELD_DATA_SYNC_ERROR',
      error: error.message
    });
  }
}

// Sync projects (placeholder for future implementation)
async function syncProjects() {
  console.log('Project sync not yet implemented');
}

// Sync rodales (placeholder for future implementation)
async function syncRodales() {
  console.log('Rodales sync not yet implemented');
}

// Notify clients about sync status
async function notifyClientsAboutSync(data) {
  const clients = await self.clients.matchAll();
  
  clients.forEach(client => {
    client.postMessage(data);
  });
}

// Show sync completion notification
async function showSyncNotification(syncedCount, failedCount) {
  const title = 'SIF-GT - Sincronización';
  let body;
  
  if (failedCount === 0) {
    body = `${syncedCount} registro${syncedCount !== 1 ? 's' : ''} sincronizado${syncedCount !== 1 ? 's' : ''} correctamente`;
  } else {
    body = `${syncedCount} sincronizado${syncedCount !== 1 ? 's' : ''}, ${failedCount} fallido${failedCount !== 1 ? 's' : ''}`;
  }
  
  const options = {
    body: body,
    icon: '/logo192.png',
    badge: '/favicon.ico',
    tag: 'sync-notification',
    requireInteraction: false,
    actions: [
      {
        action: 'view',
        title: 'Ver detalles'
      }
    ]
  };
  
  await self.registration.showNotification(title, options);
}

// Message event handler for client communication
self.addEventListener('message', event => {
  console.log('Service Worker received message:', event.data);
  
  switch (event.data.type) {
    case 'SKIP_WAITING':
      self.skipWaiting();
      break;
    case 'GET_VERSION':
      event.ports[0].postMessage({ version: VERSION });
      break;
    case 'CLEAR_CACHE':
      clearAllCaches().then(() => {
        event.ports[0].postMessage({ success: true });
      }).catch(error => {
        event.ports[0].postMessage({ success: false, error: error.message });
      });
      break;
    case 'FORCE_SYNC':
      self.registration.sync.register(SYNC_TAGS.FIELD_DATA);
      break;
    default:
      console.log('Unknown message type:', event.data.type);
  }
});

// Clear all caches (for debugging/maintenance)
async function clearAllCaches() {
  const cacheNames = await caches.keys();
  const deletePromises = cacheNames
    .filter(name => name.startsWith(CACHE_PREFIX))
    .map(name => caches.delete(name));
  
  await Promise.all(deletePromises);
  console.log('All caches cleared');
}

// Handle push notifications
self.addEventListener('push', event => {
  console.log('Push notification received');
  
  let notificationData = {
    title: 'SIF-GT',
    body: 'Datos sincronizados correctamente',
    icon: '/logo192.png',
    badge: '/favicon.ico',
    tag: 'sif-gt-notification'
  };
  
  if (event.data) {
    try {
      const data = event.data.json();
      notificationData = { ...notificationData, ...data };
    } catch (error) {
      notificationData.body = event.data.text();
    }
  }
  
  event.waitUntil(
    self.registration.showNotification(notificationData.title, {
      body: notificationData.body,
      icon: notificationData.icon,
      badge: notificationData.badge,
      tag: notificationData.tag,
      requireInteraction: false,
      actions: [
        {
          action: 'view',
          title: 'Ver aplicación'
        },
        {
          action: 'dismiss',
          title: 'Cerrar'
        }
      ]
    })
  );
});

// Handle notification clicks
self.addEventListener('notificationclick', event => {
  event.notification.close();
  
  if (event.action === 'view' || !event.action) {
    event.waitUntil(
      self.clients.matchAll({ type: 'window' }).then(clients => {
        // Check if there's already a window/tab open
        for (const client of clients) {
          if (client.url === self.registration.scope && 'focus' in client) {
            return client.focus();
          }
        }
        
        // If no window is open, open a new one
        if (self.clients.openWindow) {
          return self.clients.openWindow('/');
        }
      })
    );
  }
});

// Handle notification close
self.addEventListener('notificationclose', event => {
  console.log('Notification closed:', event.notification.tag);
});