import React, {
  HTMLAttributes,
  ReactNode,
  RefCallback,
  useEffect,
  useMemo,
  useRef,
} from 'react';
import { st, classes } from './SlidingModal.st.css.js';
import { scopeTab } from './scopeTab';

export interface SlidingModalProps {
  dataHook?: string;
  width: string | number;
  isOpen?: boolean;
  style?: HTMLAttributes<HTMLElement>['style'];
  className?: HTMLAttributes<HTMLElement>['className'];
  children?: ReactNode;
  contentRef?: RefCallback<HTMLDivElement>;
  onExited?: () => void;
  onEntered?: () => void;
  onEntering?: () => void;
  onExiting?: () => void;
  transitionStatus?: string;
  onRequestClose?: () => void;
  shadowWidth?: number;
  appendTo?: 'parent';
  overlay?: boolean;
}

export function SlidingModal(props: SlidingModalProps) {
  const {
    dataHook,
    className,
    style: styleProp,
    shadowWidth = 20,
    width,
    isOpen,
    children,
    contentRef,
    onExited,
    onEntered,
    onEntering,
    onExiting,
    onRequestClose,
    transitionStatus,
    appendTo,
    overlay,
  } = props;

  const style = useMemo(
    () => ({
      ...styleProp,
      transform: `translate3d(${
        isOpen
          ? 0
          : typeof width === 'number'
          ? `${width + shadowWidth}px`
          : `${width}px`
      }, 0, 0)`,
    }),
    [styleProp, width, isOpen],
  );

  const isOpenRef = useRef(isOpen);
  const divRef = useRef<HTMLDivElement>();

  useEffect(() => {
    if (process.env.NODE_ENV === 'test' && isOpen) {
      onEntered?.();
    } else {
      if (process.env.NODE_ENV === 'test' && isOpenRef.current) {
        onExited?.();
      }
    }

    isOpenRef.current = isOpen;
  }, [isOpen]);

  useEffect(() => {
    const rootElement = divRef.current;

    if (!rootElement) {
      return;
    }

    const onTransitionEnd = (e: TransitionEvent) => {
      if (e.propertyName === 'transform') {
        isOpenRef.current ? onEntered?.() : onExited?.();
      }
    };

    const onTransitionStart = (e: TransitionEvent) => {
      if (e.propertyName === 'transform') {
        isOpenRef.current ? onEntering?.() : onExiting?.();
      }
    };

    rootElement.addEventListener('transitionend', onTransitionEnd);
    rootElement.addEventListener('transitionstart', onTransitionStart);

    return () => {
      rootElement.removeEventListener('transitionend', onTransitionEnd);
      rootElement.removeEventListener('transitionstart', onTransitionStart);
    };
  }, []);

  return (
    <div
      tabIndex={-1}
      onKeyDown={(e) => {
        if (e.nativeEvent.code === 'Escape') {
          onRequestClose?.();
          return;
        }
        if (divRef.current && e.nativeEvent.code === 'Tab') {
          scopeTab(divRef.current, e);
          return;
        }
      }}
      ref={(el) => {
        if (el) {
          divRef.current = el;
        }

        contentRef?.(el);
      }}
      data-hook={dataHook}
      data-transition-status={transitionStatus}
      className={st(
        classes.root,
        {
          exited: transitionStatus === 'exited',
          overlay,
          appendTo,
        },
        ...[className, isOpen ? 'cairo-sliding-modal-is-open' : undefined],
      )}
      role="presentation"
      style={style}
      aria-modal={undefined}
    >
      {children}
    </div>
  );
}
