import CenteredSpinner from '@inject/shared/components/CenteredSpinner'
import { httpHello, wsGraphql } from '@inject/shared/config'
import type { FC, ReactNode } from 'react'
import { useEffect, useState } from 'react'
import ErrorDialog from './ErrorDialog'
import { host, useHost } from './host'
import { useWs, ws } from './ws'

interface ConnectionProps {
  host: string
  children: ReactNode
}

enum ConnectionStatus {
  CONNECTED,
  FAILED,
  CONNECTING,
}

const Connection: FC<ConnectionProps> = ({ host: host_, children }) => {
  const hostVar = useHost()
  const wsVar = useWs()

  const [state, setState] = useState<ConnectionStatus>(
    ConnectionStatus.CONNECTING
  )

  useEffect(() => {
    if (state === ConnectionStatus.CONNECTED && (!hostVar || !wsVar)) {
      // resets back to connecting state if host or ws is not set
      setState(ConnectionStatus.CONNECTING)
      return
    }
    /*
     * If the state is connected, we don't need to do anything
     * If the state is connecting, we need to check if the host is set
     * If the state is failed, we need to show the dialog
     */
    switch (state) {
      case ConnectionStatus.CONNECTED:
        console.log('Connected to backend!')
        break
      case ConnectionStatus.CONNECTING:
        /*
         * Connection initialization
         * populates ws and host if not set
         * checks if the server is reachable
         * sets the state to connected if successful
         * sets the state to failed if unsuccessful
         */
        if (!hostVar) {
          host(host_)
        }
        if (!wsVar) {
          ws(wsGraphql(hostVar || host_))
        }
        fetch(httpHello(hostVar || host_), {
          credentials: 'include',
          method: 'GET',
        })
          .then(() => {
            setState(ConnectionStatus.CONNECTED)
          })
          .catch(() => {
            setState(ConnectionStatus.FAILED)
          })
        break
      case ConnectionStatus.FAILED:
        /*
         * Connection failed
         * shows the dialog
         */
        break
    }
  }, [state, host_, hostVar, wsVar])

  return (
    <>
      {ConnectionStatus.CONNECTED === state && children}
      {ConnectionStatus.FAILED === state && (
        <ErrorDialog host={hostVar || host_} />
      )}
      {ConnectionStatus.CONNECTING === state && <CenteredSpinner />}
    </>
  )
}

export default Connection
