import React, { useCallback, useRef } from 'react';

import { useDialogContext } from './dialog.context';
import { DialogOverlay } from './dialog-overlay';
import {
  DialogContentBase,
  DialogContentBaseProps,
} from './dialog-content-base';

/*-- Types --*/

type BaseDialogContentProps = Omit<
  DialogContentBaseProps,
  'onPointerDownOutside' | 'onEscapeKeyDown' | 'isScrollable' | 'modal'
>;

type OnPointerDownOutside = NonNullable<
  DialogContentBaseProps['onPointerDownOutside']
>;

type OnEscapeKeyDown = NonNullable<DialogContentBaseProps['onEscapeKeyDown']>;

type OnClickOverlay = NonNullable<DialogContentBaseProps['onClick']>;

export interface DialogContentProps extends BaseDialogContentProps {
  /**
   * Specifies where scroll should be on content overflow:
   * - `outside` - scroll is on the whole container (overlay).
   * - `inside` - scroll is on Dialog.Body.
   * */
  scroll?: 'inside' | 'outside';
  /** Defines whether Dialog should be closed on clicking overlay. */
  closeOnOverlayClick?: boolean;
  /** Defines whether Dialog should be closed on pressing `Esc`. */
  closeOnEsc?: boolean;
  onEsc?: OnEscapeKeyDown;
  onOverlayClick?: OnClickOverlay;
  children?: React.ReactNode;
}

/*-- Main --*/

export const DialogContent = React.forwardRef<
  HTMLDivElement,
  DialogContentProps
>(function DialogContent(
  {
    children,
    scroll = 'outside',
    closeOnOverlayClick = true,
    closeOnEsc = true,
    onEsc,
    onOverlayClick,
    forceMount,
    ...rest
  },
  ref,
) {
  const { modal, setOpen } = useDialogContext();

  const overlayRef = useRef<HTMLDivElement>(null);

  const onPointerDownOutside: OnPointerDownOutside = useCallback(event => {
    event.preventDefault();
  }, []);

  const onEscapeKeyDown: OnEscapeKeyDown = useCallback(
    event => {
      if (!closeOnEsc) {
        event.preventDefault();
      }

      onEsc && onEsc(event);
    },
    [closeOnEsc, onEsc],
  );

  const handleOverlayClick: OnClickOverlay = useCallback(
    event => {
      if (event.target !== overlayRef.current) {
        return;
      }

      if (closeOnOverlayClick) {
        setOpen(false);
      }

      onOverlayClick && onOverlayClick(event);
    },
    [setOpen, closeOnOverlayClick, onOverlayClick],
  );

  const contentUi = (
    <DialogContentBase
      {...rest}
      ref={ref}
      isScrollable={scroll === 'inside'}
      onPointerDownOutside={onPointerDownOutside}
      onEscapeKeyDown={onEscapeKeyDown}
      modal={modal}
      forceMount={forceMount}
    >
      {children}
    </DialogContentBase>
  );

  return modal ? (
    <DialogOverlay
      ref={overlayRef}
      forceMount={forceMount}
      onClick={handleOverlayClick}
    >
      {contentUi}
    </DialogOverlay>
  ) : (
    contentUi
  );
});
