import React, { createContext, FC, useCallback, useEffect, useMemo, useRef } from 'react'
import { apolloClient } from '../apollo-client'
import { gql_Channel, useCreateChannelMutation } from '../graphql'
import { urlBase64ToUint8Array } from '../utils'

export type HNSProviderProps = {
  serviceWorkerPath?: string
  publicKey?: string
  children?: React.ReactNode
  getUserToken: () => Promise<string>
}

export type HNSContextResult = {
  // isWebPushSupported: boolean
  isWebPushSubscribed: boolean
  subscribeToWebPush: () => Promise<void>
  serviceWorkerPath?: string
  publicKey?: string
  getUserToken: () => Promise<string>
}

export const HNSContext = createContext<HNSContextResult>({
  // isWebPushSupported: false,
  isWebPushSubscribed: false,
  subscribeToWebPush: () => Promise.reject(),
  getUserToken: () => Promise.reject(),
})

export const HNSProviderInner: FC<HNSProviderProps> = ({
  serviceWorkerPath = '/hns-webpush-sw.js',
  publicKey,
  children,
  getUserToken,
}) => {
  const serviceWorkerRegistration = useRef<ServiceWorkerRegistration>()
  // const isWebPushSupported = useMemo(() => {
  //   return 'serviceWorker' in navigator && 'PushManager' in window
  // }, [])

  const [isWebPushSubscribed, setIsWebPushSubscribed] = React.useState(false)

  const [createChannel, { data }] = useCreateChannelMutation({ client: apolloClient })

  const subscribeToWebPush = useCallback(async () => {
    if (!serviceWorkerRegistration.current || !publicKey) {
      return
    }

    const subscription = await serviceWorkerRegistration.current.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: publicKey ? urlBase64ToUint8Array(publicKey) : undefined,
    })

    if (subscription) {
      try {
        const jwt = await getUserToken()
        const { data, errors } = await createChannel({
          context: {
            headers: {
              authorization: `Bearer ${jwt}`,
            },
          },
          variables: {
            channel: gql_Channel.WebPush,
            details: subscription.toJSON(),
          },
        })

        if (errors) {
          throw new Error(errors[0].message)
        }

        if (data?.hc_createChannel?.id) {
          setIsWebPushSubscribed(true)
        }
      } catch (e) {
        console.error('error', e)
      }
    }
  }, [createChannel, getUserToken, publicKey])

  const contextValue = useMemo(
    () => ({
      // isWebPushSupported,
      isWebPushSubscribed,
      subscribeToWebPush,
      serviceWorkerPath,
      publicKey,
      getUserToken,
    }),
    [isWebPushSubscribed, subscribeToWebPush, serviceWorkerPath, publicKey, getUserToken]
  )

  useEffect(() => {
    let changed = false

    if ('serviceWorker' in navigator && publicKey && !changed) {
      navigator.serviceWorker
        .getRegistration(serviceWorkerPath)
        .then((registration) => {
          if (!registration) {
            navigator.serviceWorker.register(serviceWorkerPath).then((registration) => {
              serviceWorkerRegistration.current = registration

              registration.pushManager
                .getSubscription()
                .then((subscription) => {
                  if (subscription) {
                    setIsWebPushSubscribed(true)
                  }
                })
                .catch((err) => console.error('Service Worker registration failed: ', err))
            })
          } else {
            serviceWorkerRegistration.current = registration

            registration?.pushManager
              .getSubscription()
              .then((subscription) => {
                if (subscription) {
                  setIsWebPushSubscribed(true)
                }
              })
              .catch((err) => console.error('Service Worker registration failed: ', err))
          }
        })
        .catch((e) => {
          console.error('error', e)
        })
    }

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  return <HNSContext.Provider value={contextValue}>{children}</HNSContext.Provider>
}

export const HNSProvider: FC<HNSProviderProps> = (props) => {
  // if (typeof window === 'undefined') {
  //   return <HNSProviderInner {...props}>{props.children}</HNSProviderInner>
  // }

  return <HNSProviderInner {...props} />
}

export function useHNSContext() {
  return React.useContext(HNSContext)
}
