import { useState, useRef, useEffect } from 'react'
import cx from 'classnames'
import { useRouter } from 'next/router'
import { ThumbsdownIcon, ThumbsupIcon } from '@primer/octicons-react'
import { useTranslation } from 'components/hooks/useTranslation'
import { Link } from 'components/Link'
import { sendEvent, EventType } from 'components/lib/events'

import styles from './Survey.module.scss'

enum ViewState {
  START = 'START',
  YES = 'YES',
  NO = 'NO',
  END = 'END',
}

export const Survey = () => {
  const { asPath } = useRouter()
  const { t } = useTranslation('survey')
  const [state, setState] = useState<ViewState>(ViewState.START)
  const [isEmailError, setIsEmailError] = useState(false)
  const formRef = useRef<HTMLFormElement>(null)

  useEffect(() => {
    // Always reset the form if navigating to a new page because what
    // you might have said or started to say belongs exclusively to
    // to the page you started on.
    setState(ViewState.START)
  }, [asPath])

  useEffect(() => {
    // After the form is submitted we need to manually set the focus since we
    // remove the form inputs after submit.  The privacy policy link is the
    // next focusable element in the footer so we focus that.
    if (state === ViewState.END) {
      document
        .querySelector<HTMLAnchorElement>(
          'footer a[href="/github/site-policy/github-privacy-statement"]'
        )
        ?.focus()
    }
  }, [state])

  function vote(state: ViewState) {
    return () => {
      trackEvent(getFormData())
      setState(state)
    }
  }

  // Though we set `type="email"` on the email address input which gives us browser
  // validation of the field, that has accessibility issues (e.g. some screen
  // readers won't read the error message) so we need to do manual validation
  // ourselves.
  function handleEmailInputChange() {
    const emailRegex = /[^@\s.][^@\s]*@\[?[a-z0-9.-]+\]?/i
    const surveyEmail = getFormData()?.get('survey-email')?.toString()

    if (surveyEmail?.length === 0 || surveyEmail?.match(emailRegex)) {
      setIsEmailError(false)
    } else {
      setIsEmailError(true)
    }
  }

  function submit(evt: React.FormEvent) {
    evt.preventDefault()
    trackEvent(getFormData())
    if (!isEmailError) {
      setState(ViewState.END)
      setIsEmailError(false)
    }
  }

  function getFormData() {
    if (!formRef.current) return
    return new FormData(formRef.current)
  }

  return (
    <form className="f5" onSubmit={submit} ref={formRef} data-testid="survey-form">
      <h2 className="f4 mb-3">{t`able_to_find`}</h2>

      {/* Honeypot: token isn't a real field */}
      <input type="text" className="d-none" name="survey-token" aria-hidden="true" />

      {state !== ViewState.END && (
        <div className="radio-group mb-2">
          <input
            className={cx(styles.visuallyHidden, styles.customRadio)}
            id="survey-yes"
            type="radio"
            name="survey-vote"
            value="Y"
            aria-label={t`yes`}
            onChange={vote(ViewState.YES)}
            checked={state === ViewState.YES}
          />
          <label
            className={cx(
              'btn mr-1 color-border-accent-emphasis',
              state === ViewState.YES && 'color-bg-accent-emphasis'
            )}
            htmlFor="survey-yes"
          >
            <ThumbsupIcon size={16} className={state === ViewState.YES ? '' : 'color-fg-muted'} />
          </label>
          <input
            className={cx(styles.visuallyHidden, styles.customRadio)}
            id="survey-no"
            type="radio"
            name="survey-vote"
            value="N"
            aria-label={t`no`}
            onChange={vote(ViewState.NO)}
            checked={state === ViewState.NO}
          />
          <label
            className={cx(
              'btn color-border-accent-emphasis',
              state === ViewState.NO && 'color-bg-danger-emphasis'
            )}
            htmlFor="survey-no"
          >
            <ThumbsdownIcon size={16} className={state === ViewState.NO ? '' : 'color-fg-muted'} />
          </label>
        </div>
      )}

      {[ViewState.YES, ViewState.NO].includes(state) && (
        <>
          <p className="mb-3">
            <label className="d-block mb-1 f6" htmlFor="survey-comment">
              <span>
                {state === ViewState.YES && t`comment_yes_label`}
                {state === ViewState.NO && t`comment_no_label`}
              </span>
              <span className="text-normal color-fg-muted float-right ml-1">{t`optional`}</span>
            </label>
            <textarea
              className="form-control input-sm width-full"
              name="survey-comment"
              id="survey-comment"
            ></textarea>
          </p>
          <div className={cx('form-group', isEmailError ? 'warn' : '')}>
            <label className="d-block mb-1 f6" htmlFor="survey-email">
              {t`email_label`}
              <span className="text-normal color-fg-muted float-right ml-1">{t`optional`}</span>
            </label>
            <input
              type="email"
              className="form-control input-sm width-full"
              name="survey-email"
              id="survey-email"
              placeholder={t`email_placeholder`}
              onChange={handleEmailInputChange}
              aria-invalid={isEmailError}
              {...(isEmailError ? { 'aria-describedby': 'email-input-validation' } : {})}
            />
            {isEmailError && (
              <p className="note warning" id="email-input-validation">
                {t`email_validation`}
              </p>
            )}
          </div>
          <span className="f6 color-fg-muted">{t`not_support`}</span>
          <div className="d-flex flex-justify-end flex-items-center mt-3">
            <button
              type="button"
              className="btn btn-sm btn-invisible mr-3"
              onClick={() => {
                setState(ViewState.START)
                setIsEmailError(false)
              }}
            >
              Cancel
            </button>
            <button
              disabled={isEmailError}
              type="submit"
              className="btn btn-sm color-border-accent-emphasis"
            >
              {t`send`}
            </button>
          </div>
        </>
      )}

      {state === ViewState.END && (
        <p role="status" className="color-fg-muted f6" data-testid="survey-end">{t`feedback`}</p>
      )}

      <Link
        className="f6 text-normal color-fg-accent"
        href="/github/site-policy/github-privacy-statement"
        target="_blank"
      >
        {t`privacy_policy`}
      </Link>
    </form>
  )
}

function trackEvent(formData: FormData | undefined) {
  if (!formData) return
  // Nota bene: convert empty strings to undefined
  return sendEvent({
    type: EventType.survey,
    survey_token: (formData.get('survey-token') as string) || undefined, // Honeypot
    survey_vote: formData.get('survey-vote') === 'Y',
    survey_comment: (formData.get('survey-comment') as string) || undefined,
    survey_email: (formData.get('survey-email') as string) || undefined,
  })
}
