import React, { cloneElement, useRef } from 'react'

import Box from '../box'
import { KeyboardKeys, vui, VuiComponent } from '../core'
import FocusLock from '../focusLock'
import Portal from '../portal'
import { callAll, cs, KeyboardEvent, mergeRefs, MouseEvent } from '../utils'
import { ModalProvider } from './context'
import { ModalProps } from './modal.types'
import ModalBackdrop from './modalBackdrop'
import ModalContent from './modalContent'
import { manager, useModalManager } from './modalManager'

/**
 * Displays provided content on top of a dark backdrop element with fixed position.
 * By default uses Portal for content and closes when clicking backdrop or pressing 'Escape'.
 * Uses focus and scroll lock to achieve proper accessibility.
 * More about accessibility: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/dialog_role
 */
export const Modal = vui<'div', ModalProps>((props, ref) => {
  const {
    allowPinchZoom,
    animation = 'fadeDown',
    children,
    className,
    contentProps,
    disableAutoFocus,
    disableBackdropClose,
    disableEscClose,
    disablePortal,
    disableReturnFocus,
    disableScrollLock,
    disableTrapFocus,
    finalFocusRef,
    hideBackdrop,
    initialFocusRef,
    isOpen,
    onBackdropClick: onBackdropClickProp,
    onClose,
    onEsc,
    preserveScrollBar,
    size,
    variant,
    ...rest
  } = props
  const modalRef = useRef<HTMLElement>(null)
  const mouseDownTarget = useRef<EventTarget | null>(null)

  useModalManager(modalRef, isOpen, disableScrollLock)

  if (!isOpen) return null

  const onBackdropClick = (e: MouseEvent) => {
    e.stopPropagation()

    // Avoid closing modal when click starts inside the content and is released outside
    if (mouseDownTarget.current !== e.target) return

    if (!manager.isTopModal(modalRef)) return

    if (!disableBackdropClose) {
      onClose?.()
      onBackdropClickProp?.(e)
    }
  }

  const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
    if (e.key === KeyboardKeys.Escape) {
      e.stopPropagation()

      !disableEscClose && onClose?.()

      onEsc?.()
    }
  }

  const onMouseDown = (e: MouseEvent) => (mouseDownTarget.current = e.target)

  const childProps = {
    onClick: (e: any) => {
      e.stopPropagation()
      children?.props?.onClick?.(e)
    },
    tabIndex: children?.props?.tabIndex ?? -1,
  }

  const context = { isOpen, onBackdropClick, onClose, size, variant }

  return (
    <ModalProvider value={context}>
      <Portal disablePortal={disablePortal}>
        <Box
          className={cs('vui-modal', className)}
          ref={mergeRefs(ref, modalRef)}
          {...rest}
          onKeyDown={callAll(props.onKeyDown, onKeyDown)}
          onMouseDown={callAll(props.onMouseDown, onMouseDown)}
        >
          {!hideBackdrop && <ModalBackdrop />}
          <FocusLock
            autoFocus={!disableAutoFocus}
            finalFocusRef={finalFocusRef}
            initialFocusRef={initialFocusRef}
            isDisabled={disableTrapFocus}
            returnFocus={!disableReturnFocus}
          >
            <ModalContent animation={animation} {...contentProps}>
              {cloneElement(children, childProps)}
            </ModalContent>
          </FocusLock>
        </Box>
      </Portal>
    </ModalProvider>
  )
}) as VuiComponent<'div', ModalProps> & {
  Backdrop: typeof ModalBackdrop
  Content: typeof ModalContent
}

Modal.Backdrop = ModalBackdrop
Modal.Content = ModalContent
Modal.displayName = 'Modal'

export default Modal
