import type { EmailTemplate } from '@inject/graphql/fragments/EmailTemplate.generated'
import type { FileInfo } from '@inject/graphql/fragments/FileInfo.generated'
import { useDeleteEmailDraft } from '@inject/graphql/mutations/clientonly/DeleteEmailDraft.generated'
import { useSetEmailDraft } from '@inject/graphql/mutations/clientonly/SetEmailDraft.generated'
import { useGetEmailTemplateLazyQuery } from '@inject/graphql/queries/GetEmailTemplate.generated'
import { useGetFileInfoLazyQuery } from '@inject/graphql/queries/GetFileInfo.generated'
import { useGetEmailDraftLazyQuery } from '@inject/graphql/queries/clientonly/GetEmailDraft.generated'
import { GetEmailDraftsDocument } from '@inject/graphql/queries/clientonly/GetEmailDrafts.generated'
import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
import notEmpty from '@inject/shared/utils/notEmpty'
import type { Dispatch, SetStateAction } from 'react'
import { useCallback, useState } from 'react'

export interface FormStateInput {
  loadDraft: boolean
  emailThreadId: string | undefined
  senderAddress?: string
  selectedContacts?: string[]
  bccSelectedContacts?: string[]
  subject?: string | undefined
  template?: EmailTemplate | undefined
  activateMilestone?: string
  deactivateMilestone?: string
  content?: string
  fileInfo?: FileInfo | undefined
}

export interface FormState {
  senderAddress: string
  setSenderAddress: Dispatch<SetStateAction<string>>
  selectedContacts: string[]
  setSelectedContacts: Dispatch<SetStateAction<string[]>>
  bccSelectedContacts: string[]
  setBccSelectedContacts: Dispatch<SetStateAction<string[]>>
  subject: string | undefined
  setSubject: Dispatch<SetStateAction<string | undefined>>
  template: EmailTemplate | undefined
  setTemplate: Dispatch<SetStateAction<EmailTemplate | undefined>>
  activateMilestone: string
  setActivateMilestone: Dispatch<SetStateAction<string>>
  deactivateMilestone: string
  setDeactivateMilestone: Dispatch<SetStateAction<string>>
  content: string
  setContent: Dispatch<SetStateAction<string>>
  fileInfo: FileInfo | undefined
  setFileInfo: Dispatch<SetStateAction<FileInfo | undefined>>
  storeDraft: () => void
  discardDraft: () => void
  reload: (input: FormStateInput) => void
}

const useFormState = ({
  teamId,
  inInstructor,
  teamAddress,
}: {
  teamId: string
  inInstructor: boolean
  teamAddress?: string
}): FormState => {
  const [emailThreadId, setEmailThreadId] = useState<string | undefined>()

  const [senderAddress, setSenderAddress] = useState<string>(teamAddress || '')
  const [selectedContacts, setSelectedContacts] = useState<string[]>([])
  const [bccSelectedContacts, setBccSelectedContacts] = useState<string[]>([])
  const [subject, setSubject] = useState<string | undefined>()

  const [template, setTemplate] = useState<EmailTemplate>()
  const [activateMilestone, setActivateMilestone] = useState<string>('')
  const [deactivateMilestone, setDeactivateMilestone] = useState<string>('')

  const [content, setContent] = useState<string>('')
  const [fileInfo, setFileInfo] = useState<FileInfo | undefined>()

  const { notify } = useNotifyContext()

  const [getDraft] = useGetEmailDraftLazyQuery()
  const [setDraft] = useSetEmailDraft()
  const [deleteDraft] = useDeleteEmailDraft()

  const [getFileInfo] = useGetFileInfoLazyQuery()
  const [getTemplate] = useGetEmailTemplateLazyQuery()

  const loadDraftCallback = useCallback(
    async (emailThreadId: string | undefined) => {
      getDraft({
        variables: {
          teamId,
          instructor: inInstructor,
          emailThreadId: emailThreadId || null,
        },
        fetchPolicy: 'no-cache',
        onCompleted: data => {
          const draft = data?.getEmailDraft
          if (!draft) {
            return
          }

          setContent(draft.content || '')
          setActivateMilestone(draft.activateMilestone || '')
          setDeactivateMilestone(draft.deactivateMilestone || '')

          if (draft.fileId) {
            getFileInfo({
              variables: { fileInfoId: draft.fileId },
              onCompleted: data => setFileInfo(data.fileInfo || undefined),
            })
          } else {
            setFileInfo(undefined)
          }
          if (draft.templateId) {
            getTemplate({
              variables: { templateId: draft.templateId },
              onCompleted: data =>
                setTemplate(data.threadTemplate || undefined),
            })
          } else {
            setTemplate(undefined)
          }

          if (draft.emailThreadId) {
            return
          }

          setSenderAddress(draft.senderAddress || teamAddress || '')
          setSelectedContacts(draft.selectedContacts?.filter(notEmpty) || [])
          setSubject(draft.subject || undefined)
        },
      })
    },
    [getDraft, getFileInfo, getTemplate, inInstructor, teamAddress, teamId]
  )

  const reload = useCallback(
    ({
      loadDraft,
      emailThreadId,
      activateMilestone,
      content,
      deactivateMilestone,
      fileInfo,
      selectedContacts,
      bccSelectedContacts,
      senderAddress,
      subject,
      template,
    }: FormStateInput) => {
      setEmailThreadId(emailThreadId)

      setSenderAddress(senderAddress || teamAddress || '')
      setSelectedContacts(selectedContacts || [])
      setBccSelectedContacts(bccSelectedContacts || [])
      setSubject(subject)
      setTemplate(template)
      setActivateMilestone(activateMilestone || '')
      setDeactivateMilestone(deactivateMilestone || '')
      setContent(content || '')
      setFileInfo(fileInfo)

      if (loadDraft) {
        loadDraftCallback(emailThreadId)
      }
    },
    [loadDraftCallback, teamAddress]
  )

  const storeDraft = () => {
    setDraft({
      variables: {
        emailDraft: {
          teamId,
          instructor: inInstructor,
          emailThreadId,
          senderAddress,
          content,
          activateMilestone,
          deactivateMilestone,
          fileId: fileInfo?.id,
          selectedContacts,
          subject,
          templateId: template?.id,
        },
      },
      refetchQueries: [GetEmailDraftsDocument],
      onError: err => {
        notify(err.message, {
          intent: 'danger',
        })
      },
    })
  }

  /**
   * Resets the form state to the initial state. If the form was loaded from
   * a draft, the draft will be discarded
   * (currently, that means updating its values to the initial values).
   */
  const discardDraft = () => {
    setSenderAddress(teamAddress || '')
    setContent('')
    setActivateMilestone('')
    setDeactivateMilestone('')
    setFileInfo(undefined)
    setSelectedContacts([])
    setSubject(undefined)
    setTemplate(undefined)

    deleteDraft({
      variables: {
        teamId,
        instructor: inInstructor,
        emailThreadId: emailThreadId || null,
      },
      refetchQueries: [GetEmailDraftsDocument],
      onError: err => {
        notify(err.message, {
          intent: 'danger',
        })
      },
    })
  }

  return {
    senderAddress,
    setSenderAddress,
    selectedContacts,
    setSelectedContacts,
    bccSelectedContacts,
    setBccSelectedContacts,
    subject,
    setSubject,
    template,
    setTemplate,
    activateMilestone,
    setActivateMilestone,
    deactivateMilestone,
    setDeactivateMilestone,
    content,
    setContent,
    fileInfo,
    setFileInfo,
    storeDraft,
    discardDraft,
    reload,
  }
}

export default useFormState
