import React, {
  useState,
  useImperativeHandle,
  useEffect,
  forwardRef,
  useRef,
} from "react";
import classNames from "classnames";
import ReactDOM, { unmountComponentAtNode, createPortal } from "react-dom";
import { StyledProps, Omit } from "../_type";
import { useConfig } from "../_util/config-context";
import { Icon } from "../icon";
import { ConfigProvider } from "../configprovider";
import { useVisibleTransition } from "../_util/use-visible-transition";
import { callBoth } from "../_util/call-both";
import { noop } from "../_util/noop";
import { getRoot, MessageTransition, limit } from "./util";
import { useIsomorphicLayoutEffect } from "../_util/use-isomorphic-layout-effect";

export interface MessageProps extends StyledProps {
  /**
   * 类型
   */
  type: "success" | "warning" | "error" | "loading";

  /**
   * 提示内容
   */
  content: React.ReactNode;

  /**
   * 自动关闭延时（单位毫秒）
   *
   * 设置为 0 时不自动关闭（`2.5.2` 支持）
   *
   * @default 3000
   */
  duration?: number;

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

interface MessageInstance {
  setVisible: React.Dispatch<React.SetStateAction<boolean>>;
}

const Message = forwardRef(function MessageShow(
  {
    type,
    content,
    duration = 3000,
    className,
    style = {},
    onClose = noop,
  }: MessageProps,
  ref: React.Ref<MessageInstance>
) {
  const { classPrefix } = useConfig();
  const [visible, setVisible] = useState(false);

  useImperativeHandle(ref, () => ({ setVisible }));

  const timerRef = useRef(null);

  // 渲染之后，马上显示
  useEffect(() => {
    setVisible(true);
    return () => clearTimeout(timerRef.current);
  }, []);

  useIsomorphicLayoutEffect(() => {
    clearTimeout(timerRef.current);
    if (visible && duration !== 0) {
      timerRef.current = setTimeout(() => setVisible(false), duration);
    }
  }, [duration, visible]);

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

  if (!shouldContentRender) {
    return null;
  }

  const message = (
    <MessageTransition
      in={shouldContentEnter}
      onExited={callBoth(onClose, onContentExit)}
    >
      <div
        className={classNames(`${classPrefix}-toast`, className)}
        style={{ zIndex: 1100, ...style }}
        onMouseEnter={() => clearTimeout(timerRef.current)}
        onMouseLeave={() => {
          if (duration !== 0) {
            timerRef.current = setTimeout(() => setVisible(false), duration);
          }
        }}
      >
        <Icon type={type} />
        <span>{content}</span>
      </div>
    </MessageTransition>
  );

  return createPortal(message, getRoot(classPrefix));
});

Message.displayName = "Message";

interface MessageHandle {
  /**
   * 隐藏
   */
  hide: () => void;
}

export interface MessageOptions extends Omit<MessageProps, "type"> {}

let showMethod;

/**
 * API 的方式显示通知
 */
function show(
  type: MessageProps["type"] = "success",
  { onClose = noop, ...options }: MessageOptions = { content: "" }
): MessageHandle {
  if (typeof showMethod === "function") {
    return showMethod(type, options);
  }

  const el = document.createElement("div");

  const instanceRef = React.createRef<MessageInstance>();
  ReactDOM.render(
    <ConfigProvider>
      <Message
        {...options}
        type={type}
        ref={instanceRef}
        onClose={() => {
          onClose();
          unmountComponentAtNode(el);
        }}
      />
    </ConfigProvider>,
    el
  );

  const hide = () => {
    if (instanceRef.current) {
      instanceRef.current.setVisible(false);
    }
  };

  const handle = {
    hide,
    destroy: hide,
  };

  limit(handle);

  return handle;
}

export const message = {
  success: (options: MessageOptions) => show("success", options),
  warning: (options: MessageOptions) => show("warning", options),
  error: (options: MessageOptions) => show("error", options),
  loading: (options: MessageOptions) => show("loading", options),
};

// eslint-disable-next-line dot-notation
message["setShowMethod"] = method => {
  showMethod = method;
};
