import { ApolloClient, ApolloProvider, HttpLink, split } from '@apollo/client'
import { WebSocketLink } from '@apollo/client/link/ws'
import { getMainDefinition } from '@apollo/client/utilities'
import type { FC } from 'react'
import { createNetworkStatusNotifier } from 'react-apollo-network-status'
import { SubscriptionClient } from 'subscriptions-transport-ws'

import { httpGraphql } from '@inject/shared/config'
import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
import csrfFetch from '@inject/shared/utils/csrfFetch'
import { useHost } from '../connection/host'
import { useWs } from '../connection/ws'
import localSchema from '../schemas/localSchema.graphql'
import authLink from './authLink'
import cache from './cache'
import errorLink from './errorLink'
import resolvers from './resolvers'

const { link, useApolloNetworkStatus } = createNetworkStatusNotifier()
export { useApolloNetworkStatus }

interface ApolloProps {
  children: React.ReactNode
  onFailedAuth: (msg: string) => void
}

const ApolloProviderWrapped: FC<ApolloProps> = ({ children, onFailedAuth }) => {
  const ws = useWs()
  const host = useHost()
  const { notify } = useNotifyContext()

  if (!ws || !host) {
    return <p>No `WS` or `HOST` set to ApolloProviderWrapper</p>
  }

  const http = httpGraphql(host)

  const httpLink = new HttpLink({
    uri: http,
    credentials: 'include',
    fetch: csrfFetch,
  })

  const wsLink = new WebSocketLink(
    new SubscriptionClient(ws, {
      reconnect: true,
      reconnectionAttempts: 100,
    })
  )

  const client = new ApolloClient({
    link: split(
      ({ query }) => {
        const definition = getMainDefinition(query)
        return (
          definition.kind === 'OperationDefinition' &&
          definition.operation === 'subscription'
        )
      },
      wsLink,
      link
        .concat(authLink(onFailedAuth))
        .concat(errorLink(notify))
        .concat(httpLink)
    ),
    cache,
    typeDefs: localSchema,
    resolvers,
    connectToDevTools: true,
  })

  return <ApolloProvider client={client}>{children}</ApolloProvider>
}

export default ApolloProviderWrapped
