/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
import React, { useEffect, ReactNode, useRef } from "react";
import { createPortal } from "react-dom";
import classNames from "classnames";
import { StyledProps } from "../_type";
import { Button } from "../button";
import { FadeTransition, SlideTransition } from "../transition";
import { createRocket } from "../_util/create-rocket";
import { AttachContainer, getOverlayRoot } from "../_util/get-overlay-root";
import { useVisibleTransition } from "../_util/use-visible-transition";
import { callBoth } from "../_util/call-both";
import { ModalMessage } from "./ModalMessage";
import { useOutsideClick } from "../_util/use-outside-click";
import { useConfig } from "../_util/config-context";
import { uuid } from "../_util/uuid";
import { zStack } from "../_util/z-stack";
import { BackDrop } from "../backdrop";
import { useOverlayFocus } from "../_util/use-overlay-focus";
import { KeyMap } from "../_util/key-map";
import { useTranslation } from "../i18n";
import { mergeRefs } from "../util";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { modalEventEmitter } from "./event";

/**
 * 对话框组件配置
 */
export interface ModalProps extends StyledProps {
  /**
   * 对话框内容
   */
  children?: ReactNode;

  /**
   * 对话框是否可见
   */
  visible?: boolean;

  /**
   * 对话框的标题
   * 如果禁用关闭图标的同时，没有传入对话框标题，则不渲染标题
   */
  caption?: string | JSX.Element;

  /**
   * 对话框尺寸，决定对话框的宽度
   *   - `"s"` 小尺寸对话框，宽度 480px
   *   - `"m"` 默认尺寸对话框，宽度 550px
   *   - `"l"` 大尺寸对话框，宽度 800px
   *   - `"xl"` 超大尺寸对话框，宽度 950px
   *   - `"auto"` 自动适应宽度
   *   - 传入数字/百分比可以自定义宽度
   */
  size?: "s" | "m" | "l" | "xl" | "auto" | number | string;

  /**
   * 对话框关闭时回调
   */
  onClose?: () => void;

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

  /**
   * 默认 ESC 键会触发 `onClose` 的调用，设置为 `true` 禁止该行为
   * @default false
   */
  disableEscape?: boolean;

  /**
   * 是否禁用关闭图标
   * 如果禁用关闭图标的同时，没有传入对话框标题，则不渲染标题
   * @default false
   */
  disableCloseIcon?: boolean;

  /**
   * 是否启用点击遮罩关闭
   * @default false
   */
  maskClosable?: boolean;

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

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

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

/**
 * 全局配置
 */
export interface ModelConfigOptions
  extends Pick<
    ModalProps,
    | "size"
    | "disableEscape"
    | "disableCloseIcon"
    | "maskClosable"
    | "destroyOnClose"
    | "popupContainer"
  > {}

const modelConfigOptions: ModelConfigOptions = {};

// 容器们
const ModalHeader = createRocket("ModalHeader", "div.@{prefix}-dialog__header");
const ModalBody = createRocket("ModalBody", "div.@{prefix}-dialog__body");
const ModalFooter = createRocket(
  "ModalFooter",
  "div.@{prefix}-dialog__footer",
  "div.@{prefix}-dialog__btnwrap"
);

/**
 * 模态对话框组件
 */
export const Modal = forwardRefWithStatics(
  function Modal(props: ModalProps, fRef: React.Ref<HTMLDivElement>) {
    const {
      visible,
      caption,
      size,
      onClose,
      onExited,
      disableEscape,
      disableCloseIcon,
      maskClosable,
      destroyOnClose = true,
      className,
      style,
      children,
      popupContainer,
      maskStyle,
    } = Object.assign({}, modelConfigOptions, props);
    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(() => {
      if (maskClosable && visible && zStack.top === idRef.current && onClose) {
        onClose();
      }
    });

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

    // 监听 ESC 键
    useEffect(() => {
      if (!visible) {
        return () => null;
      }
      const handleKeydown = (evt: KeyboardEvent) => {
        if (
          evt.key === KeyMap.Esc &&
          !disableEscape &&
          zStack.top === idRef.current &&
          onClose
        ) {
          onClose();
        }
      };
      window.addEventListener("keydown", handleKeydown);
      return () => window.removeEventListener("keydown", handleKeydown);
    }, [visible, disableEscape, onClose]);

    useEffect(() => {
      if (firstRender.current) {
        modalEventEmitter.emit(visible ? "open" : "close");
      }
    }, [visible]);

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

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

    useOverlayFocus(visible, focusRef);

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

    // 有标题，或者有图标，就需要渲染 header
    const hasHeader = Boolean(caption) || !disableCloseIcon;

    // 内置尺寸名称
    let sizeClassName: string = null;
    if (
      typeof size === "string" &&
      ["s", "l", "xl", "auto"].indexOf(size) > -1
    ) {
      sizeClassName = `size-${size}`;
    }

    const dialog = (
      <div
        className={`${classPrefix}-overlay ${classPrefix}-dialog-parent`}
        style={{
          display: !destroyOnClose && !contentIn ? "none" : undefined,
        }}
      >
        <FadeTransition in={shouldContentEnter} opacity={1}>
          <BackDrop style={maskStyle} />
        </FadeTransition>
        <div
          tabIndex={0}
          aria-hidden="true"
          style={{ width: 0, height: 0, overflow: "hidden", outline: "none" }}
          ref={focusRef}
        />
        <SlideTransition
          unmountOnExit={destroyOnClose}
          in={shouldContentEnter}
          onExited={callBoth(onExited, onContentExit)}
        >
          <div
            className={classNames(`${classPrefix}-dialog`, className)}
            style={style}
            onClick={e => e.stopPropagation()}
            role="dialog"
          >
            <div
              ref={mergeRefs(ref, fRef)}
              className={classNames(
                `${classPrefix}-dialog__inner`,
                sizeClassName
              )}
              style={
                size > 0 || /%$/.test(String(size)) ? { width: size } : null
              }
              {...ignoreProps}
            >
              {hasHeader && (
                <ModalHeader>
                  {caption && (
                    <h3 className={`${classPrefix}-dialog__headertitle`}>
                      {caption}
                    </h3>
                  )}
                  {!disableCloseIcon && (
                    <Button
                      icon="close"
                      htmlType="button"
                      aria-label={t.closeText}
                      onClick={onClose}
                    />
                  )}
                </ModalHeader>
              )}
              {children}
            </div>
          </div>
        </SlideTransition>
        <div
          tabIndex={0}
          aria-hidden="true"
          style={{ width: 0, height: 0, overflow: "hidden", outline: "none" }}
          onFocus={() => {
            if (focusRef.current) {
              focusRef.current.focus();
            }
          }}
        />
      </div>
    );

    return createPortal(
      dialog,
      getOverlayRoot(popupContainer || globalPopupContainer)
    );
  },
  {
    /**
     * 渲染对话框的主要内容
     */
    Body: ModalBody,

    /**
     * 渲染对话框的底部内容
     */
    Footer: ModalFooter,

    /**
     * 对话框消息内容，可置于 Body:
     */
    Message: ModalMessage,

    /**
     * 全局配置
     */
    config: (options: ModelConfigOptions = {}): void => {
      Object.assign(modelConfigOptions, options);
    },
  }
);

Modal.displayName = "ModalMain";
