import React, {
  useState,
  FunctionComponent,
  createContext,
  useContext,
  useEffect,
  useLayoutEffect,
  useRef,
  memo,
  NamedExoticComponent,
  ComponentProps,
} from 'react'
import classNames from 'classnames'

const TrayContext = createContext({})

const TrayControlsBase: FunctionComponent<{
  children: (data: any) => any
}> = ({ children }) => {
  const [open, setOpen] = useState(false)
  return (
    <TrayContext.Provider
      value={{
        close: () => setOpen(false),
        isOpen: open,
      }}
    >
      {children({
        close: () => setOpen(false),
        open: () => setOpen(true),
      })}
    </TrayContext.Provider>
  )
}

export const TrayControls = memo(TrayControlsBase)

const TrayBase: FunctionComponent<{
  padding: boolean
  position: 'left' | 'right' | 'top' | 'bottom' | 'center'
  isOpen?: boolean
  isRelativePosition?: boolean
  close?: () => void
  backdrop: boolean
  ariaLabel: string
  ariaDescId: string
  role?: 'dialog' | 'alertdialog'
  className?: string
  isInfoOnly?: boolean
}> = (props) => {
  const ctx = useContext(TrayContext)
  const closeButtonRef = useRef<HTMLButtonElement>(null)
  const {
    children,
    padding,
    position,
    close,
    isOpen,
    isRelativePosition = false,
    backdrop,
    className,
    isInfoOnly,
    ariaDescId,
    ariaLabel,
    role = 'dialog',
  } = {
    ...ctx,
    ...props,
  }

  useLayoutEffect(() => {
    if (!isOpen || !isInfoOnly || !closeButtonRef.current) return
    closeButtonRef.current.focus()
  }, [isOpen, isInfoOnly])

  useEffect(() => {
    if (!isOpen) return
    const handleKeyDown = (e: KeyboardEvent) =>
      e.which === 27 /* Escape Key */ && isOpen && close && close()
    window.addEventListener('keydown', handleKeyDown)
    return () => window.removeEventListener('keydown', handleKeyDown)
  }, [close, isOpen]) // Only evaluate when isOpen changes

  return (
    <div
      tabIndex={-1}
      role={role}
      aria-label={ariaLabel}
      aria-describedby={ariaDescId}
      className={classNames('ui-tray ui-position--fill', className, {
        'ui-tray--open': isOpen,
        'ui-tray--negative-margin': isRelativePosition,
        'ui-position--fixed': !isRelativePosition,
      })}
      {...(isOpen ? { 'aria-modal': 'true' } : { 'aria-hidden': 'true' })}
    >
      {backdrop && (
        <div
          aria-hidden="true"
          className="ui-tray__backdrop ui-position--fill ui-position--absolute ui-cursor--pointer"
          onClick={() => close && close()}
        />
      )}
      <div
        className={classNames(
          `ui-tray__container ui-tray__container--position-${position}`,
          {
            'ui-position--relative': isRelativePosition,
          }
        )}
      >
        {close ? (
          <button
            ref={closeButtonRef}
            className="ui-link--button ui-position--fixed ui-position--upper-right ui-p--0"
            aria-label="close"
            onClick={() => close && close()}
            tabIndex={isOpen ? 0 : -1}
          >
            <span className="ui-tray__close ui-icon ui-icon--close ui-scale ui-scale--1 ui-color--paper-darkest ui-p--1 ui-cursor--pointer" />
          </button>
        ) : null}

        <div
          className={classNames(
            'ui-tray__content',
            padding ? 'ui-p--3-tablet ui-p--2' : ''
          )}
        >
          {children}
        </div>
      </div>
    </div>
  )
}

TrayBase.defaultProps = {
  padding: true,
  position: 'left',
  backdrop: true,
}

export const Tray: NamedExoticComponent<ComponentProps<typeof TrayBase>> =
  memo(TrayBase)
