import ExerciseListComponent from '@/components/ExerciseList'
import { FormGroup, NonIdealState, Spinner, Switch } from '@blueprintjs/core'
import type { Exercise } from '@inject/graphql/fragments/Exercise.generated'
import { useDeleteExercise } from '@inject/graphql/mutations/DeleteExercise.generated'
import { useStartExercise } from '@inject/graphql/mutations/StartExercise.generated'
import { useStopExercise } from '@inject/graphql/mutations/StopExercise.generated'
import { GetExercisesDocument } from '@inject/graphql/queries/GetExercises.generated'
import { GetRunningExercisesDocument } from '@inject/graphql/queries/GetRunningExercises.generated'
import useExercisesSubscription from '@inject/graphql/utils/useExercisesSubscription'
import ErrorMessage from '@inject/shared/components/ErrorMessage'
import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
import notEmpty from '@inject/shared/utils/notEmpty'
import type { FC } from 'react'
import { useCallback, useMemo, useState } from 'react'

interface ExerciseListProps {
  className?: string
}

const ExerciseList: FC<ExerciseListProps> = ({ className }) => {
  const { data, loading, error } = useExercisesSubscription()

  const [showFinished, setShowFinished] = useState(true)
  const exercises = useMemo(
    () =>
      (data?.exercises ?? [])
        .filter(notEmpty)
        .filter(
          exercise => showFinished || (!showFinished && !exercise.finished)
        ),
    [data, showFinished]
  )

  const [mutateStart, { loading: loadingStart }] = useStartExercise()
  const [mutatePause, { loading: loadingPause }] = useStopExercise()
  const [mutateDelete, { loading: loadingDelete }] = useDeleteExercise()

  const { notify } = useNotifyContext()

  const runningAny = useMemo(
    () => exercises.some(exercise => exercise.running),
    [exercises]
  )

  const handleStart = useCallback(
    (exercise: Exercise) => {
      mutateStart({
        variables: {
          id: exercise.id,
        },
        refetchQueries: [GetExercisesDocument, GetRunningExercisesDocument],
        onError: err => {
          notify(err.message, { intent: 'danger' })
        },
      })
    },
    [mutateStart, notify]
  )
  const handlePause = useCallback(
    (exercise: Exercise) => {
      mutatePause({
        variables: {
          id: exercise.id,
        },
        refetchQueries: [GetExercisesDocument, GetRunningExercisesDocument],
        onError: err => {
          notify(err.message, { intent: 'danger' })
        },
      })
    },
    [mutatePause, notify]
  )
  const handleDelete = useCallback(
    (exercise: Exercise) => {
      mutateDelete({
        variables: {
          id: exercise.id,
        },
        refetchQueries: [GetExercisesDocument, GetRunningExercisesDocument],
        onError: err => {
          notify(err.message, { intent: 'danger' })
        },
      })
    },
    [mutateDelete, notify]
  )

  const exerciseList = useMemo(
    () =>
      exercises.length === 0 ? (
        <NonIdealState
          icon='low-voltage-pole'
          title='No exercise found'
          description='There are no exercises to display'
        />
      ) : (
        <ExerciseListComponent
          className={className}
          exercises={exercises}
          type='managing'
          details={(exercise: Exercise) => ({
            runningAny,
            onStart: () => handleStart(exercise),
            onPause: () => handlePause(exercise),
            onDelete: () => handleDelete(exercise),
            startLoading: loadingStart,
            pauseLoading: loadingPause,
            deleteLoading: loadingDelete,
          })}
        />
      ),
    [
      className,
      exercises,
      handleDelete,
      handlePause,
      handleStart,
      loadingDelete,
      loadingPause,
      loadingStart,
      runningAny,
    ]
  )

  if (loading) {
    return <Spinner />
  } else if (error) {
    return (
      <ErrorMessage>
        <h1>Error occurred!</h1>
        <p>{error.message}</p>
      </ErrorMessage>
    )
  } else if (!data || !data.exercises) {
    return (
      <NonIdealState
        icon='low-voltage-pole'
        title='No data'
        description='Please wait for the data to come in'
      />
    )
  }

  return (
    <div>
      <FormGroup>
        <Switch
          checked={showFinished}
          label='Show finished exercises'
          onChange={() => setShowFinished(prev => !prev)}
        />
      </FormGroup>

      {exerciseList}
    </div>
  )
}

export default ExerciseList
