/// <reference lib="webworker" />
// 自定义 Service Worker（injectManifest 模式）。
// install 时逐个抓取预缓存清单中的资源，并向所有页面广播进度，
// 用于驱动首屏的全屏加载进度条；之后断网也能完整发牌。
export {}

declare const self: ServiceWorkerGlobalScope & typeof globalThis

interface PrecacheEntry {
  url: string
  revision: string | null
}

// injectManifest 会把构建产物清单注入到这里
const MANIFEST = self.__WB_MANIFEST as PrecacheEntry[]

// 由清单内容派生缓存版本号：任一资源变化版本即变，旧缓存自动淘汰
function hashStr(s: string): string {
  let h = 5381
  for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) >>> 0
  return h.toString(36)
}
const VERSION = hashStr(MANIFEST.map((e) => `${e.url}@${e.revision ?? ''}`).join('|'))
const CACHE = `werewolf-precache-${VERSION}`

const INDEX = new URL('index.html', self.location.href).href
const URLS = Array.from(new Set([INDEX, ...MANIFEST.map((e) => new URL(e.url, self.location.href).href)]))

async function broadcast(message: unknown): Promise<void> {
  const clients = await self.clients.matchAll({ includeUncontrolled: true, type: 'window' })
  for (const client of clients) client.postMessage(message)
}

self.addEventListener('install', (event) => {
  event.waitUntil(
    (async () => {
      const cache = await caches.open(CACHE)
      const total = URLS.length
      let loaded = 0
      await broadcast({ type: 'precache-start', total })

      const queue = [...URLS]
      const CONCURRENCY = 6
      const worker = async () => {
        for (let url = queue.shift(); url !== undefined; url = queue.shift()) {
          try {
            const res = await fetch(url, { cache: 'no-cache' })
            if (res.ok) await cache.put(url, res.clone())
          } catch {
            // 单个资源失败不阻塞安装，下次启动或运行时再补
          }
          loaded++
          await broadcast({ type: 'precache-progress', loaded, total })
        }
      }
      await Promise.all(Array.from({ length: CONCURRENCY }, worker))

      await broadcast({ type: 'precache-done', total })
      await self.skipWaiting()
    })(),
  )
})

self.addEventListener('activate', (event) => {
  event.waitUntil(
    (async () => {
      const keys = await caches.keys()
      await Promise.all(
        keys.filter((k) => k.startsWith('werewolf-precache-') && k !== CACHE).map((k) => caches.delete(k)),
      )
      await self.clients.claim()
    })(),
  )
})

self.addEventListener('fetch', (event) => {
  const req = event.request
  if (req.method !== 'GET') return
  const url = new URL(req.url)
  if (url.origin !== self.location.origin) return

  // 导航请求：优先网络（便于上线后拿到新版），断网回退缓存的 index.html
  if (req.mode === 'navigate') {
    event.respondWith(
      (async () => {
        try {
          return await fetch(req)
        } catch {
          const cache = await caches.open(CACHE)
          return (await cache.match(INDEX)) ?? Response.error()
        }
      })(),
    )
    return
  }

  // 其它同源资源：cache-first，未命中再走网络并回填
  event.respondWith(
    (async () => {
      const cache = await caches.open(CACHE)
      const hit = await cache.match(req.url)
      if (hit) return hit
      try {
        const res = await fetch(req)
        if (res.ok && res.type !== 'opaque') cache.put(req.url, res.clone())
        return res
      } catch {
        return hit ?? Response.error()
      }
    })(),
  )
})
