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 { Button } from "../button";
import { Justify } from "../justify";
import { Icon, IconProps } from "../icon";
import { Text, TextProps } from "../text";
import { ConfigProvider } from "../configprovider";
import { useVisibleTransition } from "../_util/use-visible-transition";
import { callBoth } from "../_util/call-both";
import { noop } from "../_util/noop";
import { getRoot, NotificationTransition, limit } from "./util";
import { getPlainText } from "../_util/getPlainText";
import { useIsomorphicLayoutEffect } from "../_util/use-isomorphic-layout-effect";
import { useTranslation } from "../i18n";

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

  /**
   * 通知标题
   */
  title?: React.ReactNode;

  /**
   * 通知内容
   */
  description?: React.ReactNode;

  /**
   * 标题右侧渲染内容
   */
  extra?: React.ReactNode;

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

  /**
   * 底部区域点击事件
   *
   * **当该属性不为空时底部区域将变为可点击样式**
   */
  onFooterClick?: (event: React.MouseEvent<HTMLDivElement>) => void;

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

  /**
   * 相同内容的通知同时是否只展示一份
   * @default false
   * @since 2.5.0
   */
  unique?: boolean;

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

  /**
   * 点击通知时回调
   * @since 2.4.2
   */
  onClick?: (event: React.MouseEvent) => void;

  /**
   * 通知关闭动画结束时回调
   * @deprecated
   */
  onExited?: () => void;
}

interface NotificationInstance {
  setVisible: React.Dispatch<React.SetStateAction<boolean>>;
  renew: () => void;
}

const themeMap: {
  [key in NotificationProps["type"]]: [
    IconProps["type"],
    TextProps<"p">["theme"]
  ];
} = {
  // type: [icon, theme]
  success: ["success", "success"],
  warning: ["warning", "warning"],
  error: ["error", "danger"],
};

const Notification = forwardRef(function NotificationShow(
  {
    type,
    title,
    extra,
    description,
    footer,
    duration = 5000,
    className,
    style,
    onClose = noop,
    onClick = noop,
    onFooterClick,
  }: NotificationProps,
  ref: React.Ref<NotificationInstance>
) {
  const { classPrefix } = useConfig();
  const t = useTranslation();
  const [visible, setVisible] = useState(false);

  const timerRef = useRef(null);

  useImperativeHandle(ref, () => ({
    setVisible,
    renew: () => {
      clearTimeout(timerRef.current);
      if (visible && duration !== 0) {
        timerRef.current = setTimeout(() => setVisible(false), duration);
      }
    },
  }));

  // 渲染之后，马上显示
  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 hasHeader = !!title || !!extra;
  const [icon, theme] = themeMap[type] || themeMap.success;

  const content = (
    <NotificationTransition
      in={shouldContentEnter}
      onExited={callBoth(onClose, onContentExit)}
    >
      <div
        className={classNames(
          `${classPrefix}-notification`,
          `${classPrefix}-notification--${type}`,
          className
        )}
        style={style}
        onMouseEnter={() => clearTimeout(timerRef.current)}
        onMouseLeave={() => {
          if (duration !== 0) {
            timerRef.current = setTimeout(() => setVisible(false), duration);
          }
        }}
        onClick={onClick}
      >
        <div className={`${classPrefix}-notification__close`}>
          <Button
            type="icon"
            icon="close"
            htmlType="button"
            onClick={() => {
              setVisible(false);
            }}
            aria-label={t.closeText}
          />
        </div>
        {hasHeader && (
          <div className={`${classPrefix}-notification__header`}>
            <Justify
              left={
                <div className={`${classPrefix}-notification__header-title`}>
                  {title}
                </div>
              }
              right={
                <div className={`${classPrefix}-notification__header-sub-info`}>
                  {extra}
                </div>
              }
            />
          </div>
        )}
        <div className={`${classPrefix}-notification__body`}>
          <Icon type={icon} />
          <Text
            theme={theme}
            className={`${classPrefix}-notification__text`}
            parent="div"
          >
            {description}
          </Text>
        </div>
        {!!footer && (
          <div
            className={classNames(`${classPrefix}-notification__footer`, {
              [`${classPrefix}-notification__footer--link`]: onFooterClick,
            })}
            onClick={onFooterClick || noop}
          >
            {onFooterClick ? (
              <Justify
                left={
                  <Text
                    theme="label"
                    className={`${classPrefix}-fz-reset`}
                    parent="div"
                  >
                    {footer}
                  </Text>
                }
                right={<Icon type="arrowright--line" />}
              />
            ) : (
              footer
            )}
          </div>
        )}
      </div>
    </NotificationTransition>
  );

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

Notification.displayName = "Notification";

interface NotificationHandle {
  /**
   * 关闭并销毁当前对话框
   */
  destroy: () => void;
}

export interface NotificationOptions extends Omit<NotificationProps, "type"> {}

let showMethod;

/**
 * API 的方式显示通知
 */
function show(
  type: NotificationProps["type"] = "success",
  { onClose = noop, onExited = noop, ...options }: NotificationOptions = {}
): NotificationHandle {
  if (typeof showMethod === "function") {
    return showMethod(type, options);
  }
  const el = document.createElement("div");

  const instanceRef = React.createRef<NotificationInstance>();

  const show = () => {
    ReactDOM.render(
      <ConfigProvider>
        <Notification
          {...options}
          type={type}
          ref={instanceRef}
          onClose={() => {
            onClose();
            onExited();
            unmountComponentAtNode(el);
          }}
        />
      </ConfigProvider>,
      el
    );
  };

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

  const renew = () => {
    if (instanceRef.current) {
      instanceRef.current.renew();
    }
  };

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

  limit({
    show,
    hide,
    renew,
    isExisted: () => !!instanceRef.current,
    identifier: options.unique
      ? [
          type,
          getPlainText(options.title),
          getPlainText(options.description),
          getPlainText(options.extra),
          getPlainText(options.footer),
        ].join("$")
      : null,
  });

  return handle;
}

export const notification = {
  success: (options: NotificationOptions) => show("success", options),
  warning: (options: NotificationOptions) => show("warning", options),
  error: (options: NotificationOptions) => show("error", options),
};

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