// /home/Autopartes/pos/static/pwa/sw.js
// Nexus POS — Service Worker
// Self-contained vanilla JS. No external imports.
//
// Bump VERSION whenever static assets change significantly.
// The fetch handler normalizes static asset URLs (strips ?v= query strings)
// so templates can use cache-busting query params freely.

const VERSION = 33;
const CACHE_NAME = 'nexus-pos-v' + VERSION;

const APP_SHELL = [
  '/pos/static/css/tokens.css',
  '/pos/static/css/common.css',
  '/pos/static/css/pos-ui.css',
  '/pos/static/js/app-init.js',
  '/pos/static/js/sidebar.js',
  '/pos/static/js/offline-banner.js',
  '/pos/static/js/sync-engine.js',
  '/pos/static/js/i18n.js',
  '/pos/static/js/kiosk.js',
  '/pos/static/js/splash-loader.js',
  '/pos/static/js/pos-utils.js',
  '/pos/static/js/pwa-install.js',
  '/pos/static/js/chat.js',
  '/pos/static/pwa/manifest.json',
  '/pos/static/pwa/icon-192.png',
  '/pos/static/pwa/icon-512.png'
];

// ─── IndexedDB helpers (offline queue) ───────────────────────────
const DB_NAME = 'nexus-offline';
const DB_VERSION = 1;
const STORE_NAME = 'pendingRequests';

function openDB() {
  return new Promise(function (resolve, reject) {
    var request = indexedDB.open(DB_NAME, DB_VERSION);
    request.onerror = function () { reject(request.error); };
    request.onsuccess = function () { resolve(request.result); };
    request.onupgradeneeded = function (event) {
      var db = event.target.result;
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true });
      }
    };
  });
}

function savePendingRequest(entry) {
  return openDB().then(function (db) {
    return new Promise(function (resolve, reject) {
      var tx = db.transaction(STORE_NAME, 'readwrite');
      var store = tx.objectStore(STORE_NAME);
      var request = store.add(entry);
      request.onsuccess = function () { resolve(request.result); };
      request.onerror = function () { reject(request.error); };
    });
  });
}

function getPendingRequests() {
  return openDB().then(function (db) {
    return new Promise(function (resolve, reject) {
      var tx = db.transaction(STORE_NAME, 'readonly');
      var store = tx.objectStore(STORE_NAME);
      var request = store.getAll();
      request.onsuccess = function () { resolve(request.result || []); };
      request.onerror = function () { reject(request.error); };
    });
  });
}

function clearPendingRequests() {
  return openDB().then(function (db) {
    return new Promise(function (resolve, reject) {
      var tx = db.transaction(STORE_NAME, 'readwrite');
      var store = tx.objectStore(STORE_NAME);
      var request = store.clear();
      request.onsuccess = function () { resolve(); };
      request.onerror = function () { reject(request.error); };
    });
  });
}

// ─── Install: pre-cache app shell ────────────────────────────────
self.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open(CACHE_NAME).then(function (cache) {
      return cache.addAll(APP_SHELL);
    }).then(function () {
      return self.skipWaiting();
    })
  );
});

// ─── Activate: purge old caches ──────────────────────────────────
self.addEventListener('activate', function (event) {
  event.waitUntil(
    caches.keys().then(function (names) {
      return Promise.all(
        names.filter(function (n) { return n !== CACHE_NAME; })
             .map(function (n) { return caches.delete(n); })
      );
    }).then(function () {
      return self.clients.claim();
    }).then(function () {
      return self.clients.matchAll({ type: 'window' }).then(function (clients) {
        clients.forEach(function (client) {
          client.postMessage({ type: 'SW_UPDATED', cacheName: CACHE_NAME });
        });
      });
    })
  );
});

// ─── Fetch strategy ──────────────────────────────────────────────
self.addEventListener('fetch', function (event) {
  var url = new URL(event.request.url);
  var req = event.request;

  // Only handle requests within /pos/ scope
  if (url.pathname.indexOf('/pos/') === -1) {
    return;
  }

  // Normalize cache key for static assets (strip query strings) so
  // catalog.js?v=5 and catalog.js?v=6 share the same cache entry.
  var cacheKey = req;
  if (/\.(js|css|png|jpg|jpeg|webp|svg|gif|ico|woff|woff2|ttf|eot|json)$/.test(url.pathname)) {
    cacheKey = new Request(url.pathname);
  }

  // Never cache auth endpoints
  if (url.pathname.indexOf('/pos/api/auth/') !== -1) {
    return;
  }

  // HTML pages -> network-first (always fresh)
  if (req.headers.get('accept') && req.headers.get('accept').indexOf('text/html') !== -1) {
    event.respondWith(networkFirst(req));
    return;
  }

  // Offline cart queue: POST /pos/api/cart/*
  if (req.method === 'POST' && url.pathname.indexOf('/pos/api/cart/') !== -1) {
    event.respondWith(
      fetch(req.clone()).then(function (response) {
        return response;
      }).catch(function () {
        return req.clone().text().then(function (bodyText) {
          var entry = {
            url: req.url,
            method: req.method,
            headers: Array.from(req.headers.entries()),
            body: bodyText,
            timestamp: Date.now()
          };
          return savePendingRequest(entry);
        }).then(function () {
          return new Response(
            JSON.stringify({ queued: true, message: 'Added to offline queue' }),
            { status: 200, headers: { 'Content-Type': 'application/json' } }
          );
        }).catch(function (err) {
          console.error('[SW] Failed to queue offline cart request:', err);
          return new Response(
            JSON.stringify({ queued: false, message: 'Failed to queue request' }),
            { status: 503, headers: { 'Content-Type': 'application/json' } }
          );
        })
      })
    );
    return;
  }

  // WhatsApp endpoints need fresh data
  if (url.pathname.indexOf('/pos/api/whatsapp/') !== -1) {
    return;
  }

  // API calls -> network-first
  if (url.pathname.indexOf('/pos/api/') !== -1) {
    event.respondWith(networkFirst(req));
    return;
  }

  // Everything else (JS, CSS, images) -> cache-first
  event.respondWith(cacheFirst(req, cacheKey));
});

function cacheFirst(request, cacheKey) {
  cacheKey = cacheKey || request;
  return caches.match(cacheKey).then(function (cached) {
    if (cached) {
      fetch(request).then(function (response) {
        if (response && response.status === 200 && request.method === 'GET') {
          caches.open(CACHE_NAME).then(function (cache) {
            cache.put(cacheKey, response);
          });
        }
      }).catch(function () {});
      return cached;
    }
    return fetch(request).then(function (response) {
      if (response && response.status === 200 && request.method === 'GET') {
        var clone = response.clone();
        caches.open(CACHE_NAME).then(function (cache) {
          cache.put(cacheKey, clone);
        });
      }
      return response;
    });
  });
}

function networkFirst(request) {
  return fetch(request).then(function (response) {
    if (response && response.status === 200 && request.method === 'GET') {
      var clone = response.clone();
      caches.open(CACHE_NAME).then(function (cache) {
        cache.put(request, clone);
      });
    }
    return response;
  }).catch(function () {
    return caches.match(request);
  });
}

// ─── Background Sync ─────────────────────────────────────────────
self.addEventListener('sync', function (event) {
  if (event.tag === 'nexus-pos-sync') {
    event.waitUntil(
      self.clients.matchAll().then(function (clients) {
        clients.forEach(function (client) {
          client.postMessage({ type: 'SYNC_REQUESTED' });
        });
      })
    );
  }

  if (event.tag === 'nexus-cart-sync') {
    event.waitUntil(
      getPendingRequests().then(function (entries) {
        if (!entries || entries.length === 0) {
          console.log('[SW] No pending cart actions to sync.');
          return;
        }
        console.log('[SW] Syncing', entries.length, 'pending cart action(s)...');
        var syncPromises = entries.map(function (entry) {
          return fetch(entry.url, {
            method: entry.method,
            headers: entry.headers.reduce(function (obj, h) {
              obj[h[0]] = h[1];
              return obj;
            }, {}),
            body: entry.body
          }).then(function (response) {
            console.log('[SW] Cart sync success for', entry.url, '- status', response.status);
            return { ok: true, id: entry.id };
          }).catch(function (err) {
            console.error('[SW] Cart sync failed for', entry.url, '-', err);
            return { ok: false, id: entry.id };
          });
        });
        return Promise.all(syncPromises).then(function (results) {
          var allOk = results.every(function (r) { return r.ok; });
          if (allOk) {
            return clearPendingRequests().then(function () {
              console.log('[SW] All cart actions synced. Pending queue cleared.');
            });
          } else {
            var failedIds = results.filter(function (r) { return !r.ok; }).map(function (r) { return r.id; });
            console.warn('[SW] Some cart actions failed. Keeping', failedIds.length, 'entries for retry.');
          }
        });
      }).catch(function (err) {
        console.error('[SW] Error during nexus-cart-sync:', err);
      })
    );
  }
});

// ─── Push Notifications ──────────────────────────────────────────
self.addEventListener('push', function (event) {
  var data = {};
  if (event.data) {
    try {
      data = event.data.json();
    } catch (e) {
      data = { title: event.data.text() };
    }
  }
  var title = data.title || 'Nexus POS';
  var options = {
    body: data.body || 'Tienes una nueva notificación del POS.',
    icon: '/pos/static/pwa/icon-192.png',
    badge: '/pos/static/pwa/icon-192.png',
    tag: data.tag || 'nexus-pos-general',
    data: data.data || { url: '/pos/sale' },
    requireInteraction: false
  };
  event.waitUntil(
    self.registration.showNotification(title, options)
  );
});

// ─── Notification Click ──────────────────────────────────────────
self.addEventListener('notificationclick', function (event) {
  event.notification.close();
  var targetUrl = event.notification.data && event.notification.data.url
    ? event.notification.data.url
    : '/pos/sale';
  event.waitUntil(
    self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (clientList) {
      for (var i = 0; i < clientList.length; i++) {
        var client = clientList[i];
        if (client.url.indexOf('/pos/') !== -1 && 'focus' in client) {
          return client.focus().then(function (focusedClient) {
            if ('navigate' in focusedClient) {
              return focusedClient.navigate(targetUrl);
            }
          });
        }
      }
      if (self.clients.openWindow) {
        return self.clients.openWindow(targetUrl);
      }
    })
  );
});

// ─── Message handler ─────────────────────────────────────────────
self.addEventListener('message', function (event) {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
  if (event.data && event.data.type === 'CLEAR_CACHES') {
    event.waitUntil(
      caches.keys().then(function (names) {
        return Promise.all(
          names.map(function (n) { return caches.delete(n); })
        );
      }).then(function () {
        console.log('[SW] All caches cleared by client request.');
        return self.clients.matchAll().then(function (clients) {
          clients.forEach(function (client) {
            client.postMessage({ type: 'CACHES_CLEARED' });
          });
        });
      })
    );
  }
});
