import React, { forwardRef, useEffect, useState, useRef } from "react";
import classNames from "classnames";
import { StyledProps } from "../_type";
import { createRocket } from "../_util/create-rocket";
import { Tooltip } from "../tooltip";
import { DropdownBox } from "../dropdown";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { useConfig } from "../_util/config-context";
import { isChildOfType } from "../_util/is-child-of-type";
import { noop } from "../_util/noop";
import { isMobile } from "../_util/is-mobile";
import { useOutsideClick } from "../_util/use-outside-click";
import { mergeRefs } from "../util";

export interface ListItemProps extends StyledProps {
  /**
   * 是否处于激活态
   */
  current?: boolean;

  /**
   * 是否处于选中态
   */
  selected?: boolean;

  /**
   * 是否处于禁用态
   */
  disabled?: boolean;

  /**
   * 点击时回调
   */
  onClick?: (evt: React.MouseEvent) => void;

  /**
   * Tooltip 说明文本
   */
  tooltip?: React.ReactNode;

  /**
   * 菜单项
   */
  children?: React.ReactNode;
}

export const ListItem = forwardRef(function ListItem(
  {
    children,
    className,
    current,
    disabled,
    selected,
    onClick,
    tooltip,
    ...props
  }: ListItemProps,
  ref: React.Ref<HTMLLIElement>
) {
  let child = (
    <li
      {...props}
      ref={ref}
      className={classNames(className, {
        "is-current": current && !selected,
        "is-disabled": disabled,
        "is-selected": selected,
      })}
      onClick={disabled ? null : onClick}
    >
      {children}
    </li>
  );
  if (tooltip) {
    child = <Tooltip title={tooltip}>{child}</Tooltip>;
  }
  return child;
});

ListItem.displayName = "ListItem";

export interface ListSubMenuProps
  extends StyledProps,
    React.LiHTMLAttributes<HTMLLIElement> {
  /**
   * 子菜单名
   */
  label?: React.ReactNode;

  /**
   * 菜单项
   */
  children?: React.ReactNode;

  /**
   * 弹出方向
   * @default "right"
   */
  placement?: "right" | "left" | "right-end" | "left-end";

  /**
   * 展开子菜单触发方式
   * @default "hover"
   * @since 2.7.0
   */
  trigger?: "hover" | "click";

  /**
   * 是否处于禁用态
   * @since 2.5.0
   */
  disabled?: boolean;

  /**
   * 菜单项内容是否展示
   *
   * 默认为鼠标 Hover 触发，可通过该属性实现受控
   * @since 2.5.0
   */
  visible?: boolean;

  /**
   * 菜单项内容展示状态变化回调
   * @since 2.5.0
   */
  onVisibleChange?: (visible: boolean) => void;

  /**
   * 是否处于激活态
   * @since 2.6.0
   */
  current?: boolean;

  /**
   * 是否处于选中态
   * @since 2.6.0
   */
  selected?: boolean;
}

export const ListSubMenu = React.forwardRef(function ListSubMenu(
  {
    label,
    children,
    placement = "right",
    trigger = "hover",
    className,
    disabled,
    visible,
    current,
    selected,
    onVisibleChange = noop,
    onMouseEnter = noop,
    onMouseLeave = noop,
    onClick = noop,
    ...props
  }: ListSubMenuProps,
  ref: React.Ref<HTMLLIElement>
) {
  const { classPrefix } = useConfig();
  const liRef = useRef<HTMLLIElement>(null);
  const enterTimerRef = useRef(null);
  const leaveTimerRef = useRef(null);

  const [hover, setHover] = useState<boolean>(false);
  const { listen } = useOutsideClick(liRef);

  useEffect(() => {
    return () => {
      clearTimeout(enterTimerRef.current);
      clearTimeout(leaveTimerRef.current);
    };
  }, []);

  const dropdownStyle = { position: undefined };
  const [basePlacement, placementModifier] = placement.split("-");

  if (basePlacement === "left") {
    Object.assign(dropdownStyle, {
      left: "auto",
      right: "100%",
    });
  }

  if (placementModifier === "end") {
    Object.assign(dropdownStyle, {
      top: undefined,
      transform: "translateY(-100%)",
      marginTop: 6,
    });
  }

  if (disabled) {
    return (
      <li
        {...props}
        className={classNames(
          `${classPrefix}-list__submenu`,
          "is-disabled",
          className,
          {
            "is-current": current,
            "is-selected": selected,
          }
        )}
      >
        {label}
      </li>
    );
  }

  const triggerProps: React.DetailedHTMLProps<
    React.LiHTMLAttributes<HTMLLIElement>,
    HTMLLIElement
  > =
    isMobile || trigger === "click"
      ? {
          onClick: event => {
            onClick(event);
            onVisibleChange(true);
            setHover(true);
          },
          onMouseEnter,
          onMouseLeave,
        }
      : {
          onClick,
          onMouseEnter: event => {
            onMouseEnter(event);
            clearTimeout(enterTimerRef.current);
            leaveTimerRef.current = setTimeout(() => {
              onVisibleChange(true);
              setHover(true);
            }, 150);
          },
          onMouseLeave: event => {
            onMouseLeave(event);
            clearTimeout(leaveTimerRef.current);
            enterTimerRef.current = setTimeout(() => {
              onVisibleChange(false);
              setHover(false);
            }, 150);
          },
        };

  if (isMobile || trigger === "click") {
    listen(() => {
      onVisibleChange(false);
      setHover(false);
    });
  }

  return (
    <li
      ref={mergeRefs(ref, liRef)}
      {...props}
      className={classNames(`${classPrefix}-list__submenu`, className, {
        "is-hover": typeof visible !== "undefined" ? visible : hover,
        "is-current": current,
        "is-selected": selected,
      })}
      {...triggerProps}
    >
      {label}
      <DropdownBox style={dropdownStyle}>{children}</DropdownBox>
    </li>
  );
});

ListSubMenu.displayName = "ListSubMenu";

export interface ListProps extends StyledProps {
  /**
   * 列表内容。使用 `<List.Item>` 来表示列表项
   */
  children?: React.ReactNode;

  /**
   * 列表类型
   *
   * - 可以不传，表示简单平铺的列表
   * - `bullet` 列表项以点号开头
   * - `number` 列表项以列表序号开头
   * - `option` 列表以菜单的形式渲染
   * - `option-group` 列表以分组菜单的形式渲染
   */
  type?: "bullet" | "number" | "option" | "option-group";

  /**
   * 列表项之间的分割方式
   *
   * - `divide` 表示使用分割线分割
   * - `stripe` 表示使用条纹背景色分割
   */
  split?: "divide" | "stripe";

  /**
   * 列表项滚动至底部的回调
   */
  onScrollBottom?: (event: React.UIEvent) => void;
}

export const List = forwardRefWithStatics(
  function List(
    {
      className,
      style,
      children,
      type,
      split,
      onScrollBottom = () => null,
    }: ListProps,
    ref: React.Ref<HTMLUListElement | HTMLOListElement>
  ) {
    const { classPrefix } = useConfig();

    const [hasSubMenu, setHasSubMenu] = useState<boolean>(false);

    useEffect(() => {
      React.Children.forEach(children, child => {
        if (isChildOfType(child, ListSubMenu)) {
          setHasSubMenu(true);
        }
      });
    }, []); // eslint-disable-line react-hooks/exhaustive-deps

    function handleBodyScroll(event: React.UIEvent<HTMLElement>) {
      const list = event.target as HTMLElement;
      const { scrollHeight, scrollTop, clientHeight } = list;
      if (scrollHeight <= Math.ceil(clientHeight + scrollTop + 1)) {
        onScrollBottom(event);
      }
    }

    const digits = String(React.Children.count(children)).length;

    const listClassName = classNames({
      [`${classPrefix}-list`]: true,
      [`${classPrefix}-list--bullet`]: type === "bullet",
      [`${classPrefix}-list--number`]: type === "number",
      [`${classPrefix}-list--option`]:
        type === "option" || type === "option-group",
      [`${classPrefix}-list--group`]: type === "option-group",
      [`${classPrefix}-list--divider`]: split === "divide",
      [`${classPrefix}-list--striped`]: split === "stripe",
      "is-2digits": type === "number" && digits === 2,
      "is-3digits": type === "number" && digits === 3,
      "is-4digits": type === "number" && digits > 3,
      [className]: className,
    });

    const Parent = type === "number" ? "ol" : "ul";

    return (
      <Parent
        ref={ref as any}
        className={listClassName}
        style={{
          // 多级菜单展开
          maxHeight: hasSubMenu ? "none" : undefined,
          ...(style || {}),
        }}
        onScroll={handleBodyScroll}
      >
        {children}
      </Parent>
    );
  },
  {
    GroupLabel: createRocket("GroupLabel", "li.@{prefix}-list__label"),
    Divider: createRocket("Divider", "li.@{prefix}-list__separate"),
    StatusTip: createRocket("TipItem", "li.@{prefix}-list__status"),
    SubMenu: ListSubMenu,
    Item: ListItem,
  }
);

List.displayName = "List";
