// Service Worker for InfoGenie App (PWA)
// 注意：PWA 必须在 HTTPS（或 localhost）下才能生效

const CACHE_VERSION = 'v3';
const PRECACHE_NAME = `infogenie-precache-${CACHE_VERSION}`;
const RUNTIME_NAME = `infogenie-runtime-${CACHE_VERSION}`;

const CORE_ASSETS = [
  '/',
  '/index.html',
  '/manifest.json',
  '/icons/icon-192.png',
  '/icons/icon-512.png',
  '/icons/icon-192-maskable.png',
  '/icons/icon-512-maskable.png',
  '/icons/apple-touch-icon.png',
  '/icons/favicon-32.png',
  '/icons/favicon-16.png'
];

async function safeAddAll(cache, urls) {
  const uniqueUrls = Array.from(new Set(urls)).filter(Boolean);
  const results = await Promise.allSettled(uniqueUrls.map(url => cache.add(url)));
  const failures = results.filter(r => r.status === 'rejected');
  if (failures.length > 0) {
    console.warn('[SW] Some assets failed to cache:', failures.length);
  }
}

async function precacheEntrypoints(cache) {
  try {
    const res = await fetch('/asset-manifest.json', { cache: 'no-store' });
    if (!res.ok) return;
    const manifest = await res.json();

    const filesObj = manifest && typeof manifest === 'object' ? manifest.files : undefined;
    const files = filesObj && typeof filesObj === 'object' ? Object.values(filesObj) : [];
    const entrypoints = Array.isArray(manifest.entrypoints) ? manifest.entrypoints : [];

    const urls = [...files, ...entrypoints]
      .filter(p => typeof p === 'string')
      .map(p => (p.startsWith('/') ? p : `/${p}`))
      .filter(p => !p.endsWith('.map'));

    await safeAddAll(cache, urls);
  } catch (err) {
    console.warn('[SW] Failed to precache entrypoints:', err);
  }
}

self.addEventListener('install', event => {
  self.skipWaiting();
  event.waitUntil(
    (async () => {
      const cache = await caches.open(PRECACHE_NAME);
      await safeAddAll(cache, CORE_ASSETS);
      await precacheEntrypoints(cache);
    })()
  );
});

self.addEventListener('activate', event => {
  event.waitUntil(
    (async () => {
      const cacheNames = await caches.keys();
      await Promise.all(
        cacheNames.map(name => {
          if (name !== PRECACHE_NAME && name !== RUNTIME_NAME) {
            return caches.delete(name);
          }
          return undefined;
        })
      );

      await self.clients.claim();
    })()
  );
});

function isNavigationRequest(request) {
  return request.mode === 'navigate' || request.destination === 'document';
}

function shouldHandleRequest(url, request) {
  if (request.method !== 'GET') return false;
  if (url.origin !== self.location.origin) return false;
  return true;
}

self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  if (!shouldHandleRequest(url, event.request)) return;

  // 不缓存后端 API（如需缓存请在这里加规则）
  if (url.pathname.startsWith('/api')) return;

  // 页面请求：优先网络，离线回退到缓存
  if (isNavigationRequest(event.request)) {
    event.respondWith(
      (async () => {
        try {
          const networkResponse = await fetch(event.request);
          const cache = await caches.open(RUNTIME_NAME);
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        } catch (err) {
          const cached = await caches.match(event.request);
          return cached || caches.match('/index.html');
        }
      })()
    );
    return;
  }

  // 静态资源：stale-while-revalidate
  event.respondWith(
    (async () => {
      const cached = await caches.match(event.request);
      const cache = await caches.open(RUNTIME_NAME);

      const networkFetch = (async () => {
        try {
          const response = await fetch(event.request);
          if (response && response.ok) {
            cache.put(event.request, response.clone());
          }
          return response;
        } catch (err) {
          return undefined;
        }
      })();

      if (cached) {
        event.waitUntil(networkFetch);
        return cached;
      }

      const response = await networkFetch;
      if (response) return response;

      return new Response('', { status: 504, statusText: 'Offline' });
    })()
  );
});

self.addEventListener('message', event => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting();
  }
});
