// const VER = 18

const CACHE_NAME = 'cache-v1'
const TEMP_PAGE_URL = '/preloader.html'
const STATIC_ASSETS = [TEMP_PAGE_URL, '/css/preloader.css']

self.addEventListener('install', (event) => {
  event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS)))
})

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) =>
      Promise.all(
        cacheNames.map((cache) => {
          if (cache !== CACHE_NAME) {
            return caches.delete(cache)
          }
        }),
      ),
    ),
  )
})

self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url)

  // Обрабатываем только HTTP/HTTPS запросы
  if (url.protocol !== 'http:' && url.protocol !== 'https:') {
    return
  }

  if (event.request.mode === 'navigate') {
    // Для навигационных запросов сначала отдаём `/preloader.html`, пока основная страница загружается
    event.respondWith(handleNavigationRequest(event.request))
  } else if (STATIC_ASSETS.includes(url.pathname)) {
    // Кэшируем только preloader.html и его CSS
    event.respondWith(caches.match(event.request))
  }
})

const TEMP_CACHE = new Map() // Используем карту для временного хранения

async function handleNavigationRequest(request) {
  if (TEMP_CACHE.has(request.url) && navigator.onLine) {
    return TEMP_CACHE.get(request.url).clone()
  }

  const cache = await caches.open(CACHE_NAME)

  // Отдаём временную страницу из кеша
  const tempResponse = await cache.match(TEMP_PAGE_URL)

  // Попытка загрузить основную страницу в фоновом режиме
  fetch(request)
    .then(async (networkResponse) => {
      if (networkResponse && networkResponse.ok) {
        // Сохраняем клон в TEMP_CACHE
        TEMP_CACHE.set(request.url, networkResponse)

        // Сообщаем клиенту, что основная страница готова
        console.log('notifying clients about MAIN_PAGE_READY after fetch')
        const allClients = await clients.matchAll({ type: 'window' })
        allClients.forEach((client) =>
          client.postMessage({
            type: 'MAIN_PAGE_READY',
          }),
        )

        // Удаляем из TEMP_CACHE через минимальное время
        setTimeout(() => {
          TEMP_CACHE.delete(request.url)
        }, 5000)
      } else {
        throw new Error('Failed to fetch main page')
      }
    })
    .catch(async (error) => {
      console.error('Error loading main page:', error)

      // Сообщаем клиенту об ошибке сети
      const allClients = await clients.matchAll({ type: 'window' })
      allClients.forEach((client) =>
        client.postMessage({
          type: 'NETWORK_ERROR',
        }),
      )
    })

  // Возвращаем временную страницу
  return tempResponse
}

async function updateStaticAssetsCache() {
  const cache = await caches.open(CACHE_NAME)

  // Обновляем ресурсы
  try {
    await Promise.all(
      STATIC_ASSETS.map(async (asset) => {
        const response = await fetch(asset, { cache: 'no-store' })
        if (response.ok) {
          await cache.put(asset, response.clone())
        } else {
          console.error(`Failed to fetch ${asset}:`, response.status)
        }
      }),
    )
    console.log('STATIC_ASSETS обновлены.')
  } catch (error) {
    console.error('Ошибка обновления STATIC_ASSETS:', error)
  }
}

self.addEventListener('message', async (event) => {
  if (event.data && event.data.type === 'SKIP_WAITING') {
    self.skipWaiting()
  } else if (event.data && event.data.type === 'CHECK_MAIN_PAGE_READY') {
    console.log('CHECK_MAIN_PAGE_READY:', TEMP_CACHE.size ? 'ready' : 'not ready')
    if (TEMP_CACHE.size === 0) {
      event.source.postMessage({ type: 'MAIN_PAGE_IS_NOT_READY' })
    } else {
      event.source.postMessage({ type: 'MAIN_PAGE_READY' })
    }
  } else if (event.data && event.data.type === 'CHECK_ONLINE_STATUS') {
    const isOnline = navigator.onLine // Проверяем состояние сети
    event.ports[0].postMessage({ online: isOnline }) // Отправляем результат обратно
    console.log('sent CHECK_ONLINE_STATUS online:', isOnline)
  } else if (event.data && event.data.type === 'UPDATE_STATIC_ASSETS') {
    event.waitUntil(updateStaticAssetsCache())
  } else {
    event.waitUntil(debug(Object.assign({ answer: true }, event.data)))
  }
})

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll(STATIC_ASSETS)
    }),
  )

  self.skipWaiting().then(() => debug({ installed: true }))
})

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) =>
      Promise.all(
        cacheNames.map((cache) => {
          if (cache !== CACHE_NAME) {
            return caches.delete(cache)
          }
        }),
      ),
    ),
  )

  clients.claim().then(() => debug(event.source, { activated: true }))
})

self.addEventListener('push', async (event) => {
  debug({
    event: 'push',
  })
  // PushData keys structure standart https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
  let pushData = event.data.json()
  if (!pushData || !pushData.notification.title) {
    debug({
      error: 'Received WebPush with an empty title. Received body',
    })
  }
  self.registration
    .showNotification(pushData.notification.title, pushData.notification.options)
    .then(function () {
      // You can save to your analytics fact that push was shown
      // fetch('https://your_backend_server.com/track_show?message_id=' + pushData.data.message_id);
    })
    .catch((error) => {
      debug({
        errorShowNotification: error.message,
      })
    })
})

self.addEventListener('notificationclick', (event) => {
  debug({
    event: 'notificationclick',
  })
  event.waitUntil(handleNotificationClick(event))
})

async function handleNotificationClick(event) {
  event.notification.close()

  if (!event.notification.data) {
    await debug({
      error: 'Click on WebPush with empty data, where url should be. Notification',
    })
    return
  }
  if (!event.notification.data.url) {
    await debug({ error: 'Click on WebPush without url. Notification' })
    return
  }

  await openURL(event.notification.data.url)
}

async function openURL(urlToOpen) {
  let matchedClients
  try {
    const urlToOpenObj = new URL(urlToOpen, location.origin)

    matchedClients = await clients.matchAll({ type: 'window', includeUncontrolled: true })

    try {
      for (const client of matchedClients) {
        const clientUrlObj = new URL(client.url)
        try {
          await debug(client, {
            urlToOpen,
            'client.url': client.url,
          })

          if (
            urlToOpenObj.pathname + urlToOpenObj.search !==
            clientUrlObj.pathname + clientUrlObj.search
          ) {
            continue
          }

          if (client.focused) {
            debug({ 'client.focused': true })
            return
          }

          await client.focus()
          debug({ 'client.focus()': true })
          return
        } catch (error) {
          await debug({ error: error.message })
        }
      }
    } catch (error) {
      debug({ 'get client error': error })
    }
  } catch (error) {
    debug({ 'openURL error': error })
  }

  if (matchedClients && matchedClients.length) {
    debug({ 'no matched clients': true })
  }

  try {
    const client = await clients.openWindow(urlToOpen)
    if (!client) {
      await debug({ error: 'client is null' })
    }
    debug({ 'clients.openWindow()': true })
  } catch (error) {
    await debug(matchedClients && matchedClients[0], { error: error.message })
  }
}

function debug() {}
// async function debug(...args) {
//   return fetch('https://hereconnect-debug.condev.ru/debug', {
//     method: 'POST',
//     body: JSON.stringify(args),
//   })
// }
