import React, { useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import classNames from "classnames";
import { createRocket } from "../_util/create-rocket";
import { StyledProps } from "../_type";
import { Justify } from "../justify";
import { Button } from "../button";
import { FadeTransition, SlideTransition } from "../transition";
import { useOutsideClick } from "../_util/use-outside-click";
import { H3 } from "../heading";
import { useConfig } from "../_util/config-context";
import { useVisibleTransition } from "../_util/use-visible-transition";
import { callBoth } from "../_util/call-both";
import { noop } from "../_util/noop";
import { uuid } from "../_util/uuid";
import { zStack } from "../_util/z-stack";
import { BackDrop } from "../backdrop";
import { useOverlayFocus } from "../_util/use-overlay-focus";
import { AttachContainer, getOverlayRoot } from "../_util/get-overlay-root";
import { useTranslation } from "../i18n";
import { mergeRefs } from "../util";

export interface DrawerProps extends StyledProps {
  /**
   * Drawer 是否可见
   */
  visible: boolean;

  /**
   * 点击关闭图标或抽屉外区域的回调
   */
  onClose: () => void;

  /**
   * 抽屉关闭动画结束时回调
   */
  onExited?: () => void;

  /**
   * 抽屉方向
   * @default "right"
   */
  placement?: "right" | "left";

  /**
   * 抽屉大小
   *
   * - `"m"` - 360px
   * - `"l"` - 800px
   *
   * @default "m"
   */
  size?: "m" | "l";

  /**
   * Drawer 中的内容
   */
  children?: React.ReactNode;

  /**
   * Drawer 底部内容
   */
  footer?: React.ReactNode;

  /**
   * 头部标题
   */
  title?: React.ReactNode;

  /**
   * 头部副标题/说明文字
   */
  subtitle?: React.ReactNode;

  /**
   * **\[Deprecated\]** 请使用 `subtitle` 属性
   *
   * @deprecated
   */
  subTitle?: React.ReactNode;

  /**
   * 头部右侧渲染内容
   */
  extra?: React.ReactNode;

  /**
   * 点击面板外是否收起面板
   * @default true
   */
  outerClickClosable?: boolean;

  /**
   * 是否禁用头部关闭图标
   * @default false
   */
  disableCloseIcon?: boolean;

  /**
   * 是否禁用展开/收起动效
   * @default false
   */
  disableAnimation?: boolean;

  /**
   * 关闭时是否销毁 Modal 中元素
   * @default true
   * @since 2.3.0
   */
  destroyOnClose?: boolean;

  /**
   * 是否显示遮罩
   * @default false
   * @since 2.5.3
   */
  showMask?: boolean;

  /**
   * 遮罩样式
   * @since 2.5.3
   */
  maskStyle?: React.CSSProperties;

  /**
   * 挂载组件的节点
   * @default document.body
   * @since 2.6.0
   */
  popupContainer?: AttachContainer;
}

export const Drawer = React.forwardRef(function Drawer(
  {
    className,
    style = {},
    children,
    visible,
    onClose = noop,
    onExited = noop,
    size,
    placement,
    footer,
    title,
    subTitle,
    subtitle = subTitle,
    extra,
    disableCloseIcon,
    disableAnimation,
    outerClickClosable = true,
    destroyOnClose = true,
    showMask = false,
    maskStyle = {},
    popupContainer,
  }: DrawerProps,
  fRef: React.Ref<HTMLDivElement>
) {
  const { classPrefix, popupContainer: globalPopupContainer } = useConfig();
  const t = useTranslation();

  const ref = useRef<HTMLDivElement>(null);
  const focusRef = useRef<HTMLDivElement>(null);
  const idRef = useRef(uuid());
  const firstRender = useRef(!!visible);

  const { listen, ignoreProps } = useOutsideClick(ref);
  listen(
    () =>
      outerClickClosable && visible && zStack.top === idRef.current && onClose()
  );

  if (visible && !firstRender.current) {
    firstRender.current = true;
  }

  const width = size === "l" ? 800 : 320;
  const from = placement === "left" ? 0 - width : width;
  const showHeader = title || subtitle || extra || !disableCloseIcon;

  const {
    contentIn,
    shouldContentEnter,
    shouldContentRender,
    onContentExit,
  } = useVisibleTransition(visible);

  useEffect(() => {
    if (contentIn) {
      const id = idRef.current;
      zStack.push(id);
      return () => {
        zStack.remove(id);
      };
    }
    return noop;
  }, [contentIn]);

  useOverlayFocus(visible, focusRef);

  if (
    !shouldContentRender &&
    (destroyOnClose || (!destroyOnClose && !firstRender.current))
  ) {
    return null;
  }

  return ReactDOM.createPortal(
    <div
      tabIndex={-1}
      ref={focusRef}
      className={classNames({
        [`${classPrefix}-drawer-open`]: visible,
      })}
      style={{
        display: !destroyOnClose && !contentIn ? "none" : undefined,
      }}
    >
      {showMask && (
        <FadeTransition in={shouldContentEnter} opacity={1}>
          <BackDrop
            zIndex={typeof style.zIndex !== "undefined" ? style.zIndex : 10}
            style={maskStyle}
          />
        </FadeTransition>
      )}
      <SlideTransition
        in={shouldContentEnter}
        from={[from, 0]}
        timeout={disableAnimation ? 0 : undefined}
        unmountOnExit={destroyOnClose}
        onExited={callBoth(onExited, onContentExit)}
      >
        <div
          ref={mergeRefs(ref, fRef)}
          style={style}
          className={classNames(`${classPrefix}-drawer`, className, {
            [`${classPrefix}-drawer--left`]: placement === "left",
            "size-l": size === "l",
          })}
          {...ignoreProps}
        >
          {showHeader && (
            <div className={`${classPrefix}-drawer__header`}>
              <Justify
                left={
                  <DrawerTitle
                    title={title}
                    subtitle={subtitle}
                    classPrefix={classPrefix}
                  />
                }
                right={
                  <>
                    {extra}
                    {!disableCloseIcon && (
                      <Button
                        icon="close"
                        htmlType="button"
                        onClick={onClose}
                        aria-label={t.closeText}
                      />
                    )}
                  </>
                }
              />
            </div>
          )}
          <DrawerBody>{children}</DrawerBody>
          {footer && <DrawerFooter>{footer}</DrawerFooter>}
        </div>
      </SlideTransition>
    </div>,
    getOverlayRoot(popupContainer || globalPopupContainer)
  );
});

Drawer.displayName = "Drawer";

function DrawerTitle({
  title,
  subtitle,
  classPrefix,
}: Pick<DrawerProps, "title" | "subtitle"> & { classPrefix: string }) {
  const sub =
    typeof subtitle === "string" ? (
      <span className={`${classPrefix}-card__subtitle`}>{subtitle}</span>
    ) : (
      subtitle
    );

  if (typeof title === "string") {
    return (
      <H3>
        {title}
        {sub}
      </H3>
    );
  }
  return (
    <>
      {title}
      {sub}
    </>
  );
}

DrawerTitle.displayName = "DrawerTitle";

const DrawerBody = createRocket(
  "DrawerBody",
  "div.@{prefix}-drawer__body",
  "div.@{prefix}-drawer__body-inner"
);
const DrawerFooter = createRocket(
  "DrawerFooter",
  "div.@{prefix}-drawer__footer"
);
