import { useI18n } from 'domains/i18n/hooks'
import { className } from 'lib/css'
import { useEffect, useMemo, useRef, useState } from 'preact/hooks'
import Icon from 'ui/components/layout/icon'
import InOutTransition, {
  transitionStartStates,
} from 'ui/components/widgets/in-out-transition'
import { timeout } from 'ui/hooks/focus-helper-hooks'
import { useOptionButton, useSeamlyOptions } from 'ui/hooks/seamly-hooks'
import { focusElement, getKey, keyNames } from 'ui/utils/general-utils'
import Options from './options'

const OptionsButton = () => {
  const { t } = useI18n()
  const { menuOptions, showOption, panelActive, hideOption } =
    useSeamlyOptions()
  const { id } = useOptionButton()
  const focusOutDelayTimeoutID = useRef<ReturnType<typeof setTimeout> | null>(
    null,
  )

  const [menuIsOpen, setMenuIsOpen] = useState(false)

  const toggleButton = useRef(null)
  const optionsLength = menuOptions.length
  const menuItemButtons = useRef<Array<HTMLButtonElement>>([])
  const prevMenuIsOpen = useRef(false)

  const multiMenu = optionsLength > 1
  const firstOption = menuOptions[0]
  const firstOptionName = useMemo(
    () => firstOption?.name?.trim().replace(/\s+/g, ''),
    [firstOption],
  )

  useEffect(() => {
    return () => {
      if (focusOutDelayTimeoutID.current)
        clearTimeout(focusOutDelayTimeoutID.current)
    }
  }, [])

  useEffect(() => {
    if (menuIsOpen && !prevMenuIsOpen.current) {
      requestAnimationFrame(async () => {
        await timeout(60) // Wait for next frame tick
        const firstActiveOptionIndex = menuOptions.findIndex(
          (option) => option.available,
        )

        const focusIndex =
          firstActiveOptionIndex === -1 ? 0 : firstActiveOptionIndex

        if (menuItemButtons.current)
          focusElement(menuItemButtons.current[focusIndex])
      })
    }
    prevMenuIsOpen.current = menuIsOpen
  }, [menuIsOpen, menuOptions])

  const onClickHandler = () => {
    if (panelActive) {
      hideOption()
    }
    if (multiMenu) {
      setMenuIsOpen((o) => !o)
    } else if (firstOption.available && !panelActive) {
      showOption(firstOption.name)
    }
  }

  const onMainKeyDownHandler = (e: KeyboardEvent) => {
    if (!menuIsOpen) {
      return
    }

    if (getKey(e) === keyNames.Escape) {
      setMenuIsOpen(false)
      focusElement(toggleButton.current)
    }
    if (getKey(e) === keyNames.Home) {
      if (menuItemButtons.current) focusElement(menuItemButtons.current[0])
      e.preventDefault()
    }
    if (getKey(e) === keyNames.End) {
      if (menuItemButtons.current)
        focusElement(menuItemButtons.current[optionsLength - 1])
      e.preventDefault()
    }
  }

  const onButtonKeyDownHandler = (e: KeyboardEvent) => {
    if (getKey(e) === keyNames.ArrowDown) {
      setMenuIsOpen(true)
      e.preventDefault()
    }
  }

  const onMenuItemKeyDownHandler = (e, index) => {
    if (getKey(e) === keyNames.ArrowDown) {
      const newIndex = Math.min(optionsLength - 1, index + 1)
      if (menuItemButtons.current)
        focusElement(menuItemButtons.current[index === newIndex ? 0 : newIndex])
      e.preventDefault()
    }
    if (getKey(e) === keyNames.ArrowUp) {
      const newIndex = Math.max(0, index - 1)
      if (menuItemButtons.current)
        focusElement(
          menuItemButtons.current[
            index === newIndex ? optionsLength - 1 : newIndex
          ],
        )
      e.preventDefault()
    }
  }

  const onFocusOutHandler = () => {
    if (multiMenu) {
      focusOutDelayTimeoutID.current = setTimeout(() => {
        setMenuIsOpen(false)
      }, 200)
    }
  }

  const onFocusInHandler = () => {
    if (focusOutDelayTimeoutID.current)
      clearTimeout(focusOutDelayTimeoutID.current)
  }

  const onKeyPressHandler = (e, index) => {
    const char = String.fromCharCode(e.charCode)
    const isPrintableChar = !!(char.length === 1 && char.match(/\S/))

    if (!isPrintableChar) {
      return
    }

    let foundIndex = -1

    menuOptions.forEach((option, i) => {
      if (
        i > index &&
        option.title.charAt(0).toLowerCase() === char.toLowerCase() &&
        foundIndex === -1
      ) {
        foundIndex = i
      }
    })

    if (foundIndex !== -1 && menuItemButtons.current) {
      focusElement(menuItemButtons.current[foundIndex])
    }
  }

  const onMenuItemClickHandler = (name, available) => {
    if (available) {
      setMenuIsOpen(false)
      showOption(name)
    }
  }
  if (!optionsLength) {
    return null
  }

  return (
    <div
      className={className('options__container')}
      onKeyDown={onMainKeyDownHandler}
      onFocusOut={onFocusOutHandler}
      onFocusIn={onFocusInHandler}
    >
      <InOutTransition
        transitionStartState={transitionStartStates.notRendered}
        isActive={panelActive}
      >
        <div className={className('options__dialog')}>
          <Options />
        </div>
      </InOutTransition>
      {multiMenu && (
        <InOutTransition
          isActive={menuIsOpen}
          transitionStartState={transitionStartStates.notRendered}
        >
          <ul
            className={className(['options__menu'])}
            role="menu"
            tabIndex={-1}
            aria-labelledby={id}
          >
            {menuOptions.map(({ name, title, available }, i) => (
              <li
                className={className('options__menu__item')}
                key={name}
                role="menuitem"
                data-testid={name}
              >
                <button
                  type="button"
                  ref={(item) => {
                    if (item) menuItemButtons.current[i] = item
                  }}
                  className={className(['button', 'button--secondary'])}
                  onKeyDown={(e) => onMenuItemKeyDownHandler(e, i)}
                  onKeyPress={(e) => onKeyPressHandler(e, i)}
                  onClick={() => onMenuItemClickHandler(name, available)}
                  aria-disabled={!available}
                >
                  <span className={className('button__text')}>
                    {title}
                    {!available && ' '}
                  </span>
                  {!available ? (
                    <span className={className('button__state')}>
                      {t('options.unavailableText')}
                    </span>
                  ) : null}
                </button>
              </li>
            ))}
          </ul>
        </InOutTransition>
      )}
      <button
        type="button"
        className={className([
          'button',
          'button--secondary',
          'chat__options__button',
          ...(!multiMenu && firstOptionName
            ? [`chat__options__button--${firstOptionName}`]
            : []),
        ])}
        id={id}
        onClick={onClickHandler}
        onKeyDown={multiMenu ? onButtonKeyDownHandler : undefined}
        ref={toggleButton}
        aria-haspopup={multiMenu ? 'menu' : false}
        aria-expanded={multiMenu ? menuIsOpen : false}
        aria-disabled={!multiMenu && !firstOption.available}
      >
        {multiMenu ? <Icon name="options" size="32" alt="" /> : null}
        <span className={className('button__text')}>
          {multiMenu
            ? t('options.openButtonText')
            : `${firstOption.title}${!firstOption.available ? ' ' : ''}`}
        </span>
        {!multiMenu && !firstOption.available ? (
          <span className={className('button__state')}>
            {t('options.unavailableText')}
          </span>
        ) : null}
      </button>
    </div>
  )
}

export default OptionsButton
