import React, { useState, useEffect, useRef, useCallback } from "react";
import classNames from "classnames";
import { StyledProps } from "../_type";
import { useDefault } from "../_util/use-default";
import { Icon } from "../icon";
import { FadeTransition } from "../transition";
import { useConfig } from "../_util/config-context";
import { Button } from "../button";
import { AlertNotice } from "./AlertNotice";
import { useTranslation } from "../i18n";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { callBoth } from "../util";
import { uuid } from "../_util/uuid";

export interface AlertProps extends StyledProps {
  /**
   * 提示类型
   * @default "info"
   */
  type?: "info" | "success" | "warning" | "error";

  /**
   * 提示内容
   */
  children?: React.ReactNode;

  /**
   * 控制 Alert 是否显示，如果没传，默认显示
   * 配合 `onClose` 回调可以实现关闭效果
   */
  visible?: boolean;

  /**
   * 默认是否显示。如果不想自己控制 Alert 的显示状态，可以传入 defaultVisible 为 true，此时会渲染关闭图标，并且用户点击关闭时隐藏。
   * @default true
   */
  defaultVisible?: boolean;

  /**
   * 传入 visible 或者 defaultVisible 都会渲染关闭图标，用户点击关闭图标时回调 onClose
   */
  onClose?: () => void;

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

  /**
   * 隐藏图标
   * @default false
   * @since 2.3.6
   */
  hideIcon?: boolean;

  /**
   * 轮播模式
   * @default false
   */
  carouselMode?: boolean;

  /**
   * 轮播时间间隔
   * @default 5000
   * @since 2.3.0
   */
  interval?: number;
}

const iconMap = {
  info: "infoblue",
  success: "success",
  warning: "warning",
  error: "error",
};

export const Alert = forwardRefWithStatics(
  function Alert(
    {
      type,
      children,
      className,
      onClose,
      extra,
      hideIcon,
      carouselMode,
      interval = 5000,
      visible: _visible,
      defaultVisible: _defaultVisible,
      ...props
    }: AlertProps,
    ref: React.Ref<HTMLDivElement>
  ) {
    const { classPrefix } = useConfig();
    const t = useTranslation();

    const [visible, onVisibleChange] = useDefault(
      _visible,
      _defaultVisible,
      visible => !visible && onClose && onClose()
    );

    const isClosable = typeof visible === "boolean";

    const timerRef = useRef(null);
    const [currentIndex, setCurrentIndex] = useState<number>(0);
    const { length } = React.Children.toArray(children).filter(Boolean);

    const circulate = useCallback(() => {
      if (!carouselMode) {
        return;
      }
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }
      timerRef.current = setTimeout(() => {
        setCurrentIndex(i => (i + 1) % length);
        circulate();
      }, interval);
    }, [carouselMode, interval, length]);

    useEffect(() => {
      circulate();
      return () => clearTimeout(timerRef.current);
    }, [circulate]);

    const [id, setId] = useState(uuid());
    useEffect(() => {
      setId(uuid());
    }, [children]);

    const alertClassName = classNames(
      `${classPrefix}-alert`,
      {
        [`${classPrefix}-alert--${type}`]: type,
      },
      className
    );
    const alertElement = (children: React.ReactNode) => (
      <div
        ref={ref}
        className={alertClassName}
        {...props}
        onMouseEnter={callBoth(() => {
          if (timerRef.current) {
            clearTimeout(timerRef.current);
          }
        }, (props as any)?.onMouseEnter)}
        onMouseLeave={callBoth(circulate, (props as any)?.onMouseLeave)}
      >
        {!hideIcon && (
          <span className={`${classPrefix}-alert__decoration`}>
            <Icon type={iconMap[type] || "infoblue"} />
          </span>
        )}
        <div className={`${classPrefix}-alert__info`}>{children}</div>
        <div className={`${classPrefix}-alert__operational`}>
          {extra}
          {carouselMode && (
            <Dot
              classPrefix={classPrefix}
              total={length}
              currentIndex={currentIndex}
              onChange={index => {
                setCurrentIndex(index);
                circulate();
              }}
            />
          )}
          {isClosable && (
            <Button
              type="icon"
              icon="close"
              htmlType="button"
              onClick={() => onVisibleChange(false)}
              aria-label={t.closeText}
            />
          )}
        </div>
      </div>
    );

    const content = !carouselMode
      ? alertElement(children)
      : alertElement(
          React.Children.toArray(children)
            .filter(Boolean)
            .map((c, i) => (
              <div
                key={`${id}-${i}`}
                style={i === currentIndex ? undefined : { display: "none" }}
              >
                {c}
              </div>
            ))
        );

    if (isClosable) {
      return <FadeTransition in={visible}>{content}</FadeTransition>;
    }

    return content;
  },
  {
    Notice: AlertNotice,
  }
);

Alert.displayName = "Alert";

function Dot({
  classPrefix,
  total,
  currentIndex,
  onChange,
}: {
  classPrefix: string;
  total: number;
  currentIndex: number;
  onChange: (index: number) => void;
}) {
  return (
    <div className={`${classPrefix}-alert__nav ${classPrefix}-alert__nav--dot`}>
      {Array(total)
        .fill(null)
        .map((_, i) => (
          <span
            key={i}
            className={classNames(`${classPrefix}-alert__nav-item`, {
              "is-current": currentIndex === i,
            })}
            onClick={() => onChange(i)}
          />
        ))}
    </div>
  );
}
