import type { ButtonProps, OptionProps } from '@blueprintjs/core'
import {
  Button,
  Callout,
  Divider,
  NonIdealState,
  RadioGroup,
  Spinner,
} from '@blueprintjs/core'
import { css } from '@emotion/css'
import type { Question } from '@inject/graphql/fragments/Question.generated'
import type { QuestionnaireDetails } from '@inject/graphql/fragments/QuestionnaireDetails.generated'
import { useAnswerQuestionnaire } from '@inject/graphql/mutations/AnswerQuestionnaire.generated'
import { useGetTeam } from '@inject/graphql/queries/GetTeam.generated'
import type { AnswerInput } from '@inject/graphql/types'
import useTeamQuestionnaireStateSubscription from '@inject/graphql/utils/useTeamQuestionnaireStateSubscription'
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 { Fragment, useEffect, useMemo, useState } from 'react'

const spinner = css`
  /* causes ever-changing overflows without the padding */
  padding: 1rem;
`

const wrapper = css`
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  height: 100%;
  width: 100%;
  gap: 1rem;
`

const content = css`
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
`

const body = css`
  display: grid;
  grid-template-columns: auto auto 1fr;
  row-gap: 0.5rem;
  flex: 1;
`

const footer = css`
  display: flex;
  justify-content: flex-end;
`

export const questionNumber = css`
  text-align: right;
`

export const INVALID_CHOICE = -1

export interface TraineeQuestionnaireContentProps {
  details: QuestionnaireDetails
  teamId: string
}

const TraineeQuestionnaireContent: FC<TraineeQuestionnaireContentProps> = ({
  details,
  teamId,
}) => {
  const { notify } = useNotifyContext()
  const [answers, setAnswers] = useState<AnswerInput[]>(
    details.questions.map(question => ({
      questionId: question.id,
      choice: INVALID_CHOICE,
    }))
  )

  useEffect(() => {
    setAnswers(
      details.questions.map(question => ({
        questionId: question.id,
        choice: INVALID_CHOICE,
      }))
    )
  }, [details.questions])

  const [mutate] = useAnswerQuestionnaire({
    variables: {
      questInput: { answers, questionnaireId: details.id, teamId },
    },
    onError: error => notify(error.message, { intent: 'danger' }),
  })

  // TODO: ensure this works once backend is updated
  /*
   * currently, teamData.team.userSet will always be an empty array
   * when called by a trainee
   *
   * Should be fixed by the following MR:
   * https://gitlab.fi.muni.cz/inject/backend/-/merge_requests/237
   */
  const {
    data: teamData,
    loading: teamLoading,
    error: teamError,
  } = useGetTeam({ variables: { teamId } })
  const { data, loading, error } = useTeamQuestionnaireStateSubscription(
    teamId,
    details.id
  )
  useEffect(() => {
    if (!data || !data.questionnaireState) {
      return
    }
    const teamAnswers = data.questionnaireState.answers
    setAnswers(prev =>
      prev.map(answer => ({
        ...answer,
        choice:
          teamAnswers.find(
            teamAnswer => teamAnswer.question.id === answer.questionId
          )?.choice || answer.choice,
      }))
    )
  }, [data, details.questions])

  const { status } = data?.questionnaireState || {}

  const { title, disabled } = useMemo<ButtonProps>(() => {
    if (status === 'ANSWERED') {
      return { title: 'Answers have already been submitted', disabled: true }
    }

    if (answers.some(answer => answer.choice === INVALID_CHOICE)) {
      return { title: 'Answer all questions before submitting', disabled: true }
    }

    return { title: undefined, disabled: false }
  }, [answers, status])

  if (loading || teamLoading) {
    return <Spinner className={spinner} />
  }
  if (error) {
    return (
      <ErrorMessage>
        <h1>Error occurred!</h1>
        <p>{error.message}</p>
      </ErrorMessage>
    )
  }
  if (teamError) {
    return (
      <ErrorMessage>
        <h1>Error occurred!</h1>
        <p>{teamError.message}</p>
      </ErrorMessage>
    )
  }
  if (!data?.questionnaireState || !teamData?.team) {
    return (
      <NonIdealState
        icon='low-voltage-pole'
        title='No data'
        description='Please wait for the data to come in'
      />
    )
  }
  const numUsers = teamData.team.userSet?.filter(notEmpty).length || 0

  const handleChange = (
    choice: number,
    questionId: string,
    questionIndex: number
  ) => {
    setAnswers(prev => [
      ...prev.slice(0, questionIndex),
      {
        questionId,
        choice,
      },
      ...prev.slice(questionIndex + 1),
    ])
  }

  const getOptions = (
    question: Question
  ): readonly OptionProps<string | number>[] | undefined =>
    question.labels
      ? question.labels
          .split(',')
          .map(label => label.trim())
          .map((label, labelIndex) => ({
            label: label,
            value: labelIndex + 1,
          }))
      : [...Array(question.max).keys()].map(index => ({
          label: (index + 1).toString(),
          value: index + 1,
        }))

  return (
    <div className={wrapper}>
      {status !== 'ANSWERED' && numUsers > 1 && (
        <Callout icon='warning-sign'>
          <b>Answers can be submitted only once per team!</b>
          <br />
          <span>Make sure all team members agree before submitting</span>
        </Callout>
      )}

      <div className={content}>
        <div className={body}>
          {details.questions.map((question, questionIndex) => (
            <Fragment key={question.id}>
              {details.questions.length > 1 && (
                <>
                  <div
                    className={questionNumber}
                  >{`${questionIndex + 1}.`}</div>
                  <Divider />
                </>
              )}
              <RadioGroup
                disabled={status === 'ANSWERED'}
                inline
                label={
                  <div
                    dangerouslySetInnerHTML={{
                      __html: question.content.rendered,
                    }}
                  />
                }
                selectedValue={
                  answers[questionIndex].choice === INVALID_CHOICE
                    ? undefined
                    : answers[questionIndex].choice
                }
                options={getOptions(question)}
                onChange={event =>
                  handleChange(
                    Number(event.currentTarget.value),
                    question.id,
                    questionIndex
                  )
                }
              />
            </Fragment>
          ))}
        </div>

        <div className={footer}>
          <Button
            intent='primary'
            onClick={mutate}
            disabled={disabled}
            title={title}
          >
            Submit
          </Button>
        </div>
      </div>
    </div>
  )
}

export default TraineeQuestionnaireContent
