import { useApolloClient } from '@apollo/client'
import useInInstructor from '@inject/shared/hooks/useInInstructor'
import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
import { usePopupContext } from '@inject/shared/popup/PopupContext'
import type { ReactNode } from 'react'
import { useEffect } from 'react'
import type { ActionLog } from '../fragments/ActionLog.generated'
import type { CustomInjectDetails } from '../fragments/CustomInjectDetails.generated'
import type { EmailDetails } from '../fragments/EmailDetails.generated'
import type { InjectDetails } from '../fragments/InjectDetails.generated'
import type { QuestionnaireDetails } from '../fragments/QuestionnaireDetails.generated'
import { useSetIsUnreadChannel } from '../mutations/clientonly/SetIsUnreadChannel.generated'
import { useGetTeamActionLogs } from '../queries/GetTeamActionLogs.generated'
import type { GetTeamChannelLogs } from '../queries/GetTeamChannelLogs.generated'
import { GetTeamChannelLogsDocument } from '../queries/GetTeamChannelLogs.generated'
import type { TeamAction } from '../subscriptions/TeamActionLogs.generated'
import { TeamActionDocument } from '../subscriptions/TeamActionLogs.generated'
import type { LogType } from '../types'

const isInCurrentContext = (actionLog: ActionLog): boolean => {
  switch (actionLog.type) {
    case 'EMAIL':
      return window.location.pathname.includes('/email')
    case 'FORM':
      return window.location.pathname.includes('/form')
    case 'INJECT':
    case 'CUSTOM_INJECT':
      return window.location.pathname.includes('/info')
    case 'TOOL':
      return window.location.pathname.includes('/tool')
    default:
      return false
  }
}
const getNotificationMessage = (type: LogType): string => {
  switch (type) {
    case 'EMAIL':
      return 'New email'
    case 'FORM':
      return 'New questionnaire'
    case 'INJECT':
    // fallthrough
    case 'CUSTOM_INJECT':
      return 'New inject'
    case 'TOOL':
      return 'New tool response'
    default:
      throw new Error(`Unknown log type: ${type}`)
  }
}

// TODO: refactor the component to use `useSubscription` instead
const useActionLogSubscription = ({
  teamId,
  showNotifications,
  getPopup,
}: {
  teamId: string
  showNotifications?: boolean
  getPopup?: (actionLog: ActionLog) => ReactNode
}) => {
  const [setIsUnread] = useSetIsUnreadChannel()
  const isInstructor = useInInstructor()
  const apollo = useApolloClient()
  // TODO: show 'active' overlays from query
  const query = useGetTeamActionLogs({
    variables: {
      teamId,
    },
    initialFetchPolicy: 'network-only',
    nextFetchPolicy: 'cache-and-network',
    onCompleted: data => {
      const setChannelIds = new Set<string>()
      data.actionLogs?.forEach(actionLog => {
        if (
          actionLog &&
          !isInCurrentContext(actionLog) &&
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          !(actionLog.details as any)?.overlay &&
          !actionLog.readReceipt &&
          !(actionLog.details as EmailDetails)?.readReceipt
        ) {
          setChannelIds.add(actionLog.channel.id)
        }
      })
      setChannelIds.forEach(channelId =>
        setIsUnread({
          variables: {
            channelId: channelId,
            teamId,
            isUnread: true,
          },
        })
      )
    },
  })
  const { notify } = useNotifyContext()
  const { showPopup } = usePopupContext()
  const { subscribeToMore, networkStatus } = query
  useEffect(
    () =>
      subscribeToMore({
        document: TeamActionDocument,
        variables: {
          teamId,
        },
        updateQuery: (prev, { subscriptionData }) => {
          // Note: this is a hack, because Codegen implicitly typecasts subscriptionData to the parent query type, this is not desired
          const retypedData = subscriptionData.data as unknown as TeamAction
          if (!retypedData) return prev
          const newActionLog = retypedData.actionLogs?.actionLog
          const oldActionLog = prev.actionLogs || []

          if (!newActionLog) {
            return prev
          }

          // push information to the end of channel query
          const oldChannel = apollo.cache.readQuery<GetTeamChannelLogs>({
            query: GetTeamChannelLogsDocument,
            variables: {
              teamId,
              channelId: newActionLog.channel.id,
            },
          }) || { teamChannelLogs: [] }
          apollo.cache.writeQuery<GetTeamChannelLogs>({
            query: GetTeamChannelLogsDocument,
            variables: {
              teamId,
              channelId: newActionLog.channel.id,
            },
            data: {
              teamChannelLogs: [
                ...(oldChannel.teamChannelLogs || []),
                newActionLog,
              ],
            },
          })

          // TODO: log the information whether the overlay was closed
          if (!isInstructor) {
            if (!getPopup) {
              throw new Error('getPopup is required for trainee view')
            }

            let details
            switch (newActionLog.type) {
              case 'INJECT':
                details = newActionLog.details as InjectDetails
                if (!details.overlay) {
                  break
                }

                showPopup(details.overlay.duration, getPopup(newActionLog))
                break
              case 'CUSTOM_INJECT':
                details = newActionLog.details as CustomInjectDetails
                if (!details.overlay) {
                  break
                }

                showPopup(details.overlay.duration, getPopup(newActionLog))
                break
              case 'EMAIL':
                details = newActionLog.details as EmailDetails
                if (!details.overlay) {
                  break
                }

                showPopup(details.overlay.duration, getPopup(newActionLog))
                break
              case 'FORM':
                details = newActionLog.details as QuestionnaireDetails
                if (!details.overlay) {
                  break
                }

                showPopup(details.overlay.duration, getPopup(newActionLog))
                break
              default:
                break
            }
          }

          if (
            !isInCurrentContext(newActionLog) &&
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            !(newActionLog.details as any)?.overlay
          ) {
            setIsUnread({
              variables: {
                channelId: newActionLog.channel.id,
                teamId: newActionLog.team.id,
                isUnread: true,
              },
            })
          }

          if (showNotifications) {
            notify(
              `${newActionLog.team.name}: ${getNotificationMessage(newActionLog.type)}`,
              {},
              new Date(newActionLog.timestamp || 0)
            )
          }

          return {
            ...prev,
            actionLogs: [...oldActionLog, newActionLog],
          }
        },
      }),
    [
      teamId,
      apollo,
      subscribeToMore,
      notify,
      isInstructor,
      showPopup,
      getPopup,
      setIsUnread,
      showNotifications,
    ]
  )

  return {
    networkStatus,
    teamId,
  }
}

export default useActionLogSubscription
