import React, {
  forwardRef,
  useContext,
  useEffect,
  useMemo,
  useRef,
} from "react";
import classNames from "classnames";
import scrollIntoView from "scroll-into-view-if-needed";
import { StyledProps } from "../_type";
import { callBoth } from "../_util/call-both";
import { useConfig } from "../_util/config-context";
import { Badge } from "../badge";
import { Icon } from "../icon";
import { uuid } from "../_util/uuid";
import { MenuContext, SubMenuContext } from "./MenuContext";
import { noop } from "../_util/noop";
import { isChildOfType } from "../_util/is-child-of-type";
import { mergeRefs } from "../util";

export interface MenuItemProps extends StyledProps {
  /**
   * 标题
   */
  title?: React.ReactNode;

  /**
   * 菜单折叠后标题处显示的图标 URL 或自定义节点内容
   *
   * **传入 `string` 类型将作为 URL 解析**
   *
   * **传递一组 URL 时，第一个 URL 会作为未激活态图标，第二个 URL 会作为激活态图标**
   *
   * @docType [React.ReactNode, React.ReactNode] | React.ReactNode
   */
  icon?: React.ReactNode | [React.ReactNode, React.ReactNode];

  /**
   * 标题旁标签内容
   * @since 2.3.0
   */
  tag?: React.ReactNode;

  /**
   * 是否为外部链接（显示外部链接图标）
   * @since 2.3.0
   */
  external?: boolean;

  /**
   * 是否为选中状态
   *
   * @default false
   */
  selected?: boolean;

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

  /**
   * 自定义渲染
   *
   * @default children => <a>{children}</a>
   */
  render?: (children: JSX.Element) => JSX.Element;
}

const defaultRender = children => <a>{children}</a>;

export const MenuItem = forwardRef(function MenuItem(
  {
    title,
    icon,
    tag,
    selected,
    external,
    className,
    onClick = noop,
    render = defaultRender,
    ...props
  }: MenuItemProps,
  fRef: React.Ref<HTMLLIElement>
) {
  const ref = useRef<HTMLLIElement>(null);
  const { classPrefix } = useConfig();
  const { setMobileCollapsed } = useContext(MenuContext);
  const { inSubMenu, onSelectedChange, onTagChange } = useContext(
    SubMenuContext
  );

  const idRef = useRef(uuid());

  useEffect(() => {
    if (ref.current && selected) {
      scrollIntoView(ref.current, {
        scrollMode: "if-needed",
        block: "nearest",
        inline: "nearest",
      });
    }
  }, [ref, selected]);

  useEffect(() => {
    onTagChange(idRef.current, !!tag);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [tag]);

  useEffect(
    () => () => {
      onTagChange(idRef.current, false);
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    []
  );

  useEffect(() => {
    onSelectedChange(idRef.current, selected);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selected]);

  const tagBadge = useMemo(
    () => (isChildOfType(tag, Badge) ? tag : <Badge>{tag}</Badge>),
    [tag]
  );

  const [defaultIcon, activeIcon] = Array.isArray(icon) ? icon : [icon, icon];
  const children = render(
    <>
      {defaultIcon &&
        !inSubMenu &&
        (typeof defaultIcon === "string" ? (
          <img
            className={`${classPrefix}-menu__list-icon`}
            src={defaultIcon}
            alt="icon"
          />
        ) : (
          defaultIcon
        ))}
      {activeIcon &&
        !inSubMenu &&
        (typeof activeIcon === "string" ? (
          <img
            className={`${classPrefix}-menu__list-icon is-selected`}
            src={activeIcon}
            alt="icon"
          />
        ) : (
          activeIcon
        ))}
      <div className={`${classPrefix}-menu__text`}>
        {title}
        {external && <Icon type="externallink"></Icon>}
      </div>
      {!!tag && tagBadge}
    </>
  );

  return (
    <li
      ref={mergeRefs(fRef, ref)}
      className={classNames(className, { "is-selected": selected })}
      onClick={() => setMobileCollapsed(false)}
      {...props}
    >
      {React.cloneElement(children, {
        className: classNames(
          `${classPrefix}-menu__item`,
          children.props.className
        ),
        onClick: callBoth(onClick, children.props.onClick),
      })}
    </li>
  );
});

MenuItem.displayName = "MenuItem";
