import React, { forwardRef, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { CSSProperties } from 'react';
import { IModalProps } from './type';
import { ModalPlacement, ModalSize, ModalVariant } from './constants';
import { Portal } from '@cleartrip/ct-design-portal';
import { FocusLock } from '@cleartrip/ct-design-focus-lock';
import { Transition } from '@cleartrip/ct-design-transition';
import Backdrop from './Backdrop';
import useMergeRefs from '@cleartrip/ct-design-use-merge-refs';
import { Container, ContainerRef } from '@cleartrip/ct-design-container';
import ModalContext from './ModalContext';
import Handler from './Handler';
import { makeStyles, useStyles, useWebMergeStyles } from '@cleartrip/ct-design-style-manager';

const staticModalStyles = makeStyles(() => ({
  modalContainer: {
    position: 'relative',
    display: 'flex',
    flexDirection: 'column',
    margin: 'auto',
    height: '100%',
    width: '100%',
    alignItems: 'center',
  },
  outerContainer: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
  },
}));

const handler = new Handler();
const DEFAULT_ANIMATION_TIME = 350;

const Modal = forwardRef<ContainerRef, IModalProps>(
  (
    {
      children,
      insidePortal = true,
      open = false,
      onClose,
      onKeyDown,
      hideCrossIcon,
      footerAlignment,
      size = ModalSize.MEDIUM,
      allowBackdropClose = true,
      blur = true,
      backDropComponent,
      animationDuration = DEFAULT_ANIMATION_TIME,
      placement = 'center',
      bottomMargin,
      variant,
      styleConfig = {},
      ...rest
    },
    forwardedRef,
  ) => {
    const { backdropStyles = [], modalContentStyles = [], root: rootStyles = [] } = styleConfig;
    const overflowY: CSSProperties['overflowY'] = 'auto';
    const isFullScreen = size === ModalSize.FULL_SCREEN;

    const dynamicStyles = useStyles(
      (theme) => ({
        modalContainer: {
          position: 'relative',
          zIndex: theme.zIndex.modal,
          overflowY,
          display: 'flex',
          flexDirection: 'column',
          margin: 'auto',
          height: '100%',
          width: '100%',
          justifyContent: placement === ModalPlacement.BOTTOM ? 'flex-end' : ModalPlacement.CENTER,
          alignItems: ModalPlacement.CENTER,
          paddingBottom: placement === ModalPlacement.BOTTOM ? bottomMargin || 188 : 0,
        },
        outerContainer: {
          zIndex: theme.zIndex.modal,
          /**
           * Web only properties
           */
          ...(isFullScreen ? { height: '100vh' as unknown as number, width: '100vw' as unknown as number } : {}),
        },
        childContainer: {
          boxSizing: 'content-box',
          borderRadius: size === ModalSize.FULL_SCREEN ? 0 : theme.spacing['3'],
          borderBottomLeftRadius: size === ModalSize.FULL_SCREEN ? 0 : theme.spacing['3'],
          borderBottomRightRadius: size === ModalSize.FULL_SCREEN ? 0 : theme.spacing['3'],
          backgroundColor:
            variant === ModalVariant.DARK ? theme.color.background.defaultDarkest : theme.color.background.neutral,
          width: (isFullScreen ? '100%' : '90%') as unknown as number,
          maxWidth: (isFullScreen ? '100%' : '90%') as unknown as number,
          overflowY,
          maxHeight:
            size === ModalSize.FULL_SCREEN
              ? ('100vh' as unknown as number)
              : (`calc(100vh - 64px)` as unknown as number),
          boxShadow: blur
            ? `${theme.elevation.modalBlurLight}, ${theme.elevation.modalBlurMedium}, ${theme.elevation.modalBlurHeavy}`
            : 'none',
        },
      }),
      [size, overflowY, placement, bottomMargin, isFullScreen, blur, variant],
    );

    const modalContainerClass = useWebMergeStyles(
      [staticModalStyles.modalContainer, dynamicStyles.modalContainer, ...rootStyles],
      [dynamicStyles.modalContainer, rootStyles],
    );
    const modalRef = useRef<HTMLDivElement | null>(null);
    const containerRef = useRef<{ modalRef?: typeof modalRef.current }>({});
    const [isOpen, setIsOpen] = useState(open);

    const handleRef = useMergeRefs(forwardedRef, modalRef);

    useLayoutEffect(() => {
      let timeoutId: NodeJS.Timeout | null = null;
      if (!open) {
        // to add closing animation
        timeoutId = setTimeout(() => setIsOpen(false), animationDuration);
      } else {
        setIsOpen(true);
      }

      return () => {
        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
        timeoutId && clearTimeout(timeoutId);
      };
    }, [open, animationDuration]);

    useEffect(() => {
      if (modalRef.current) {
        modalRef.current.scrollTop = 0;
      }
      if (open) {
        containerRef.current.modalRef = modalRef.current;
        handler.mount({ ref: containerRef.current.modalRef });
      }

      return () => {
        if (open) {
          handler.remove({ ref: containerRef.current.modalRef || null });
          // @ts-ignore
          // eslint-disable-next-line react-hooks/exhaustive-deps
          containerRef.current.modalRef = modalRef.current;
        }
      };
    }, [open]);

    const handleBackdropClick = (event: React.MouseEvent<HTMLDivElement>) => {
      if (event.target !== event.currentTarget) {
        return;
      }
      if (allowBackdropClose && onClose) {
        onClose();
      }
    };

    const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
      if (onKeyDown) {
        onKeyDown(event);
      }
      if (event.key !== 'Escape') {
        return;
      }

      event.stopPropagation();
      if (onClose) {
        onClose();
      }
    };

    if (!isOpen || !React.isValidElement(children)) {
      return null;
    }

    // TODO Focus trap breaks height sequence
    const modalElement = (
      <FocusLock>
        <Container
          styleConfig={{
            root: [staticModalStyles.outerContainer, dynamicStyles.outerContainer],
          }}
          {...rest}
        >
          <div className={modalContainerClass} onKeyDown={handleKeyDown} ref={handleRef}>
            {backDropComponent || (
              <Backdrop
                open={open}
                blur={blur}
                transitionDuration={350}
                onClick={handleBackdropClick}
                variant={variant}
                styleConfig={{ root: backdropStyles }}
              />
            )}
            <Transition
              type={isFullScreen ? 'slide' : 'fade'}
              slideDirection='up'
              in={open}
              timeout={animationDuration}
            >
              <Container
                styleConfig={{
                  root: [dynamicStyles.childContainer, ...modalContentStyles],
                }}
              >
                <ModalContext.Provider value={{ footerAlignment, hideCrossIcon, onClose, size }}>
                  {React.cloneElement(children, { ...children.props, onClose })}
                </ModalContext.Provider>
              </Container>
            </Transition>
          </div>
        </Container>
      </FocusLock>
    );

    if (insidePortal) {
      return <Portal id='global_modal'>{modalElement}</Portal>;
    }

    return modalElement;
  },
);

Modal.displayName = 'Modal';
export default Modal;
