import type { ExerciseLoadingPayload } from '@/events'
import DeleteAlert from '@/exercisepanel/DeleteAlert'
import StartAlert from '@/exercisepanel/StartAlert'
import { convertMinutes } from '@/utils'
import type { Intent } from '@blueprintjs/core'
import { Button, ButtonGroup, Classes } from '@blueprintjs/core'
import { css } from '@emotion/css'
import type { Exercise } from '@inject/graphql/fragment-types'
import type { VariablesOf } from '@inject/graphql/graphql'
import {
  DeleteExercise,
  StartExercise,
  StopExercise,
} from '@inject/graphql/mutations'
import { useClient } from '@inject/graphql/urql/client'
import { useEventListener } from 'ahooks'
import { useMemo, useState, type FC } from 'react'
import LinkButton from '../LinkButton'
import DownloadLogsButton from './DownloadLogsButton'
import InfoButton from './InfoButton'
import type { SelectingDetails } from './types'

const buttonGroup = css`
  .${Classes.BUTTON} {
    /* appear under the table head */
    z-index: 0 !important;
  }
`

type ExerciseButtonsProps = {
  isSelected?: boolean
  exercise: Exercise
} & (
  | {
      type: 'selecting'
      details: SelectingDetails
      runningAny?: never
    }
  | { type: 'managing'; details?: never; runningAny: boolean }
)

const executeMutation = (
  client: ReturnType<typeof useClient>,
  exerciseId: string,
  type: 'start' | 'stop' | 'delete'
) => {
  switch (type) {
    case 'start':
      return client.mutation<unknown, VariablesOf<typeof StartExercise>>(
        StartExercise,
        {
          id: exerciseId,
        }
      )
    case 'stop':
      return client.mutation<unknown, VariablesOf<typeof StopExercise>>(
        StopExercise,
        {
          id: exerciseId,
        }
      )
    case 'delete':
      return client.mutation<unknown, VariablesOf<typeof DeleteExercise>>(
        DeleteExercise,
        {
          id: exerciseId,
        }
      )
  }
}

const start = async (...params: Parameters<typeof executeMutation>) => {
  window.dispatchEvent(
    new CustomEvent('ExerciseLoadingEvent', {
      detail: {
        loading: true,
      },
    })
  )
  try {
    return await executeMutation(...params)
  } catch (err) {
    return err
  } finally {
    window.dispatchEvent(
      new CustomEvent<ExerciseLoadingPayload>('ExerciseLoadingEvent', {
        detail: {
          loading: false,
        },
      })
    )
  }
}

const useLoadingState = () => {
  const [loading, setLoading] = useState(false)
  useEventListener('ExerciseLoadingEvent', e => {
    setLoading(e.detail.loading)
  })
  return loading
}

const ExerciseButtons: FC<ExerciseButtonsProps> = ({
  isSelected,
  exercise,
  type,
  details,
  runningAny,
}) => {
  const [alertOpen, setAlertOpen] = useState(false)
  const [startAlertOpen, setStartAlertOpen] = useState(false)

  const intent: Intent | undefined = useMemo(() => {
    if (exercise.running) return 'success'
    if (isSelected) return 'primary'
    return undefined
  }, [exercise.running, isSelected])

  const client = useClient()
  const loading = useLoadingState()

  return (
    <ButtonGroup className={buttonGroup} alignText='left'>
      {type === 'selecting' ? (
        !isSelected && (
          <Button onClick={details.onSelect} intent={intent}>
            Select
          </Button>
        )
      ) : (
        <>
          {!exercise.running && !exercise.finished && (
            <>
              <Button
                intent={intent}
                icon='play'
                onClick={() => setStartAlertOpen(true)}
                loading={loading}
                disabled={runningAny}
                title={
                  runningAny
                    ? 'Only one exercise can run at a time'
                    : exercise.exerciseStart
                      ? 'Resume'
                      : 'Start'
                }
              />
              {type === 'managing' && (
                <StartAlert
                  open={startAlertOpen}
                  setOpen={setStartAlertOpen}
                  onSubmit={() => start(client, exercise.id, 'start')}
                  loading={loading}
                >
                  <p>Are you sure you want to start this exercise? </p>
                  <p>
                    The exercise will take: <br />
                    <b>{convertMinutes(exercise.config.exerciseDuration)}</b>
                  </p>
                </StartAlert>
              )}
            </>
          )}
          {exercise.running && (
            <Button
              intent={intent}
              icon='pause'
              onClick={() => start(client, exercise.id, 'stop')}
              loading={loading}
              title='Pause'
            />
          )}
          <LinkButton
            link={[
              '/exercise-panel/exercise/:exerciseId',
              { params: { exerciseId: exercise.id } },
            ]}
            button={{
              icon: 'people',
              title: 'Participants',
              intent,
            }}
          />
          <Button
            intent={intent}
            icon='trash'
            onClick={() => setAlertOpen(true)}
            disabled={exercise.running}
            title={
              exercise.running ? 'Cannot delete a running exercise' : 'Delete'
            }
            active={alertOpen}
          />
          {type === 'managing' && (
            <DeleteAlert
              open={alertOpen}
              setOpen={setAlertOpen}
              onDelete={() => start(client, exercise.id, 'delete')}
              loading={loading}
            >
              <p>
                Are you sure you want to delete the selected exercise? This
                action is irreversible.
              </p>
            </DeleteAlert>
          )}
          <DownloadLogsButton
            exerciseId={exercise.id}
            buttonProps={{
              intent,
              title: 'Download logs',
              text: undefined,
              rightIcon: undefined,
            }}
          />
        </>
      )}
      <InfoButton intent={intent} exercise={exercise} />
    </ButtonGroup>
  )
}

export default ExerciseButtons
