// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://cra.link/PWA
import {useFirebase} from "../controller"
import {IServiceWorkerConfig} from "../model"

export const useServiceWorker = () => {
  const {firebaseConfig} = useFirebase()

  const isLocalhost = () =>
    Boolean(
      window.location.hostname === "localhost" ||
        // [::1] is the IPv6 localhost address.
        window.location.hostname === "[::1]" ||
        // 127.0.0.0/8 are considered localhost for IPv4.
        window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
    )

  const register = (config: IServiceWorkerConfig) => {
    if ("serviceWorker" in navigator) {
      const queryParam = Object.entries({...firebaseConfig, scope: process.env.NEXT_PUBLIC_SCOPE || ""})
        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value || "")}`)
        .join("&")
      const swUrl = `${process.env.NEXT_PUBLIC_SCOPE || ""}/service-worker.js?${queryParam}`

      if (isLocalhost()) {
        // This is running on localhost. Let's check if a service worker still exists or not.
        checkValidServiceWorker(swUrl, config)

        // Add some additional logging to localhost, pointing developers to the
        // service worker/PWA documentation.
        navigator.serviceWorker.ready
          .then(() => {
            console.log(
              "Service Worker >>>>> This web app is being served cache-first by a service " +
                "worker. To learn more, visit https://cra.link/PWA"
            )
          })
          .catch(err => {
            console.log("Service Worker >>>>> Error", err)
          })
      } else {
        // Is not localhost. Just register service worker
        registerValidSW(swUrl, config)
      }
    }
  }

  const registerValidSW = (swUrl: string, config: IServiceWorkerConfig) => {
    navigator.serviceWorker
      .register(swUrl)
      .then(registration => {
        registration.onupdatefound = () => {
          const installingWorker = registration.installing
          if (installingWorker == null) {
            return
          }
          installingWorker.onstatechange = () => {
            if (installingWorker.state === "installed") {
              if (navigator.serviceWorker.controller) {
                // At this point, the updated precached content has been fetched,
                // but the previous service worker will still serve the older
                // content until all client tabs are closed.
                console.log(
                  "Service Worker >>>>> New content is available and will be used when all " +
                    "tabs for this page are closed. See https://cra.link/PWA."
                )

                // Execute callback
                if (config && config.onUpdate) {
                  config.onUpdate(registration)
                }
              } else {
                // At this point, everything has been precached.
                // It's the perfect time to display a
                // "Content is cached for offline use." message.
                console.log("Service Worker >>>>> Content is cached for offline use.")

                // Execute callback
                if (config && config.onSuccess) {
                  config.onSuccess(registration)
                }
              }
            }
          }
        }
      })
      .catch(error => {
        console.error("Service Worker >>>>> Error during service worker registration:", error)
      })
  }

  const checkValidServiceWorker = (swUrl: string, config: IServiceWorkerConfig) => {
    // Check if the service worker can be found. If it can't reload the page.
    fetch(swUrl, {
      headers: {"Service-Worker": "script"},
    })
      .then(response => {
        // Ensure service worker exists, and that we really are getting a JS file.
        const contentType = response.headers.get("content-type")
        if (response.status === 404 || (contentType != null && contentType.indexOf("javascript") === -1)) {
          console.error("Service Worker >>>>> Service worker not found", typeof contentType, contentType)
          // No service worker found. Probably a different app. Reload the page.
          navigator.serviceWorker.ready.then(registration => {
            registration.unregister().then(() => {
              window.location.reload()
            })
          })
        } else {
          // Service worker found. Proceed as normal.
          console.log("Service Worker >>>>> Service worker found. Proceed as normal.", response)
          registerValidSW(swUrl, config)
        }
      })
      .catch(() => {
        console.log("Service Worker >>>>> No internet connection found. App is running in offline mode.")
      })
  }

  const unregister = () => {
    if ("serviceWorker" in navigator) {
      navigator.serviceWorker.ready
        .then(registration => {
          registration.unregister()
        })
        .catch(error => {
          console.error("Service Worker >>>>> ", error.message)
        })
    }
  }

  const registerServiceWorker = () => {
    register({
      onUpdate: registration => {
        console.log("Service Worker >>>>> On register", registration)
        const waitingServiceWorker = registration.waiting
        if (waitingServiceWorker) {
          waitingServiceWorker.addEventListener("statechange", event => {
            if (event.target && event.target["state"] === "activated") {
              window.location.reload()
            }
          })
          console.log("Service Worker >>>>> Post message to skip waiting stage")
          waitingServiceWorker.postMessage({type: "SKIP_WAITING"})
        }
      },
      onSuccess: registration => {
        console.log("Service Worker >>>>> On Success: ", registration)
      },
    })
  }

  return {
    registerServiceWorker,
    unregister,
  }
}
