/**
 * ProxmoxPanel — WebSocket Service Worker
 * Maintient les connexions WS en vie entre les navigations Swup.
 * Les pages s'abonnent/désabonnent via postMessage.
 */
'use strict'

// channel → { ws: WebSocket|null, url: string, clientIds: Set<string>, retryDelay: number }
const connections = new Map()

self.addEventListener('install', () => self.skipWaiting())

self.addEventListener('activate', e =>
  e.waitUntil(self.clients.claim())
)

self.addEventListener('message', event => {
  const { type, channel, url, payload } = event.data || {}
  if (!channel) return
  const clientId = event.source?.id
  if (!clientId) return

  switch (type) {
    case 'WS_SUBSCRIBE': {
      let conn = connections.get(channel)
      if (!conn) {
        conn = { ws: null, url: null, clientIds: new Set(), retryDelay: 2000 }
        connections.set(channel, conn)
      }
      conn.clientIds.add(clientId)
      if (url) conn.url = url
      ensureWS(channel)
      break
    }
    case 'WS_UNSUBSCRIBE': {
      const conn = connections.get(channel)
      if (conn) conn.clientIds.delete(clientId)
      break
    }
    case 'WS_SEND': {
      const conn = connections.get(channel)
      if (conn?.ws?.readyState === 1) conn.ws.send(payload)
      break
    }
  }
})

function ensureWS(channel) {
  const conn = connections.get(channel)
  if (!conn?.url) return
  if (conn.ws && conn.ws.readyState < 2) return // CONNECTING (0) ou OPEN (1)

  const ws = new WebSocket(conn.url)
  conn.ws = ws
  conn.retryDelay = 2000

  notify(channel, 'WS_STATUS', { status: 'connecting' })

  ws.onopen = () => {
    conn.retryDelay = 2000
    notify(channel, 'WS_STATUS', { status: 'connected' })
  }

  ws.onmessage = e => notify(channel, 'WS_MESSAGE', { data: e.data })

  ws.onerror = () => notify(channel, 'WS_STATUS', { status: 'error' })

  ws.onclose = () => {
    notify(channel, 'WS_STATUS', { status: 'disconnected' })
    // Backoff exponentiel plafonné à 30s
    const delay = conn.retryDelay
    conn.retryDelay = Math.min(conn.retryDelay * 1.5, 30000)
    setTimeout(() => ensureWS(channel), delay)
  }
}

async function notify(channel, type, extra) {
  const conn = connections.get(channel)
  if (!conn || conn.clientIds.size === 0) return
  const allClients = await self.clients.matchAll({ type: 'window' })
  for (const client of allClients) {
    if (conn.clientIds.has(client.id)) {
      client.postMessage({ channel, type, ...extra })
    }
  }
}
