import React, {
  useState,
  useImperativeHandle,
  forwardRef,
  useRef,
  useEffect,
} from "react";
import classNames from "classnames";
import { TreeContext, TreeContextValue } from "./TreeContext";
import { Check } from "../../check";
import { Icon } from "../../icon";
import { StyledProps } from "../../_type";
import { injectValue } from "../../_util/inject-value";
import { useConfig } from "../../_util/config-context";
import { noop } from "../../_util/noop";
import { forwardRefWithStatics } from "../../_util/forward-ref-with-statics";
import { callBoth } from "../../util";

export const TreeActionLink = React.forwardRef(function TreeActionLink(
  {
    children,
    className,
    ...props
  }: React.AnchorHTMLAttributes<HTMLAnchorElement>,
  ref: React.Ref<HTMLAnchorElement>
) {
  const { classPrefix } = useConfig();
  return (
    <a
      ref={ref}
      className={classNames(`${classPrefix}-text--link`, className)}
      {...props}
    >
      {children}
    </a>
  );
});

TreeActionLink.displayName = "TreeActionLink";

export interface TreeNodeProps extends StyledProps {
  /**
   * 节点 ID，整棵树中唯一
   */
  id: string;

  /**
   * 节点内容
   */
  content: React.ReactNode;

  /**
   * 节点图标
   *
   * @docType React.ReactNode | (context: { expanded: boolean }) => React.ReactNode
   */
  icon?:
    | ((context: { expanded: boolean }) => React.ReactNode)
    | React.ReactNode;

  /**
   * hover 后展示的操作
   *
   * 推荐使用文字（TreeNode.ActionLink），若超过一项，建议收至 Dropdown 下
   */
  operation?: React.ReactNode;

  /**
   * 当树为 selectable 时，设置当前节点是否展示 Checkbox
   *
   * @default true
   */
  selectable?: boolean;

  /**
   * 当树为 selectable 时，设置当前节点 Checkbox 是否禁用
   *
   * @default false
   */
  disableSelect?: boolean;

  /**
   * 当前节点是否支持展开（用于异步加载）
   *
   * @default false
   */
  expandable?: boolean;

  /**
   * 当前节点是否不需要附加样式（如 Hover 样式）
   *
   * @default false
   * @since 2.2.4
   */
  pure?: boolean;

  /**
   * 包含的树节点 <TreeNode />
   */
  children?: React.ReactNode;
}

export const TreeNode = forwardRefWithStatics(
  function TreeNode(props: TreeNodeProps, ref: React.Ref<HTMLDivElement>) {
    return (
      <TreeContext.Consumer>
        {context => {
          return <TreeNodeInner ref={ref} {...context} {...props} />;
        }}
      </TreeContext.Consumer>
    );
  },
  {
    ActionLink: TreeActionLink,
  }
);

TreeNode.displayName = "TreeNode";

export const TreeNodeInner = React.forwardRef(function TreeNodeInner(
  {
    id,
    content,
    icon,
    operation,
    selectable,
    activable,
    activeIds,
    onActive,
    expandable,
    children,
    expandedIds,
    fullExpandable,
    onExpand,
    onLoad,
    onLoadError,
    switcherIcon,
    pure,
    disableSelect, // 外层使用
    className,
    style,
    ...props
  }: TreeNodeProps & TreeContextValue,
  ref: React.Ref<HTMLDivElement>
) {
  const { classPrefix } = useConfig();
  const expanded = expandedIds.includes(id);
  const nodeIcon = injectValue(icon)({ expanded });
  const switcherRef = useRef<TreeNodeSwitcherInstance>(null);

  function handleExpand(event: React.MouseEvent) {
    if (switcherRef.current) {
      switcherRef.current.handleExpand(event);
    }
  }

  // activable 与 selectable 同时开启时 Checkbox 与内容结构分离
  const nodeContent =
    activable && selectable ? (
      <div className={`${classPrefix}-tree__label`}>
        <Check name={id} type="checkbox" />
        <span
          className={`${classPrefix}-tree__label-title`}
          onClick={() => onActive(id)}
        >
          {nodeIcon}
          {content}
          {operation && (
            <div className={`${classPrefix}-tree__action`}>{operation}</div>
          )}
        </span>
      </div>
    ) : (
      <div
        className={`${classPrefix}-tree__label`}
        onClick={
          fullExpandable && Boolean(expandable || children)
            ? handleExpand
            : noop
        }
      >
        {selectable ? (
          <Check name={id} type="checkbox">
            {nodeIcon}
            {content}
          </Check>
        ) : (
          <>
            {nodeIcon}
            <span className={`${classPrefix}-tree__label-title`}>
              {content}
            </span>
          </>
        )}
        {operation && (
          <div className={`${classPrefix}-tree__action`}>{operation}</div>
        )}
      </div>
    );

  return (
    <li
      className={classNames(`${classPrefix}-tree__node`, className, {
        // 高亮
        "is-selected": activable && activeIds.includes(id),
        [`${classPrefix}-tree__node--transparent`]: pure,
      })}
      style={style}
    >
      <div
        ref={ref}
        className={`${classPrefix}-tree__node-content`}
        style={activable ? { cursor: "pointer" } : undefined}
        // 额外 props 暂时透传至内容节点，等待节点结构改为非嵌套
        {...props}
        onClick={callBoth(
          () => !selectable && activable && onActive(id),
          (props as any).onClick
        )}
      >
        <TreeNodeSwitcher
          id={id}
          ref={switcherRef}
          icon={switcherIcon}
          enable={Boolean(expandable || children)}
          expanded={expanded}
          onExpand={onExpand}
          onLoad={onLoad}
          onLoadError={onLoadError}
          classPrefix={classPrefix}
        />
        {nodeContent}
      </div>
      {expanded && (
        <ul className={`${classPrefix}-tree__subtree`}>{children}</ul>
      )}
    </li>
  );
});

TreeNodeInner.displayName = "TreeNodeInner";

interface TreeNodeSwitcherProps {
  id: TreeNodeProps["id"];
  icon: TreeContextValue["switcherIcon"];
  enable: boolean;
  expanded: boolean;
  onExpand: TreeContextValue["onExpand"];
  onLoad: TreeContextValue["onLoad"];
  onLoadError: TreeContextValue["onLoadError"];
  classPrefix: string;
}

interface TreeNodeSwitcherInstance {
  handleExpand: (event: React.MouseEvent) => Promise<void>;
}

const TreeNodeSwitcher = forwardRef(function TreeNodeSwitcher(
  {
    id,
    enable,
    expanded,
    onExpand,
    onLoad,
    onLoadError = () => null,
    icon = ({ expanded }) => (
      <Icon type={expanded ? "arrowdown" : "arrowright"} />
    ),
    classPrefix,
  }: TreeNodeSwitcherProps,
  ref: React.Ref<TreeNodeSwitcherInstance>
) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);
  const onExpandRef = useRef(onExpand);

  useEffect(() => {
    onExpandRef.current = onExpand;
  }, [onExpand]);

  const switcherIcon = injectValue(icon)({ expanded, nodeId: id });

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

  async function handleExpand(event: React.MouseEvent) {
    event.stopPropagation();
    if (expanded) {
      onExpand(id, false);
      return;
    }

    if (onLoad) {
      setError(false);
      setLoading(true);
      try {
        await onLoad(id);
        // 外层 expand 状态变化导致 onExpand 更新
        onExpandRef.current(id, true);
      } catch (err) {
        setError(true);
        onLoadError(id, err);
      } finally {
        setLoading(false);
      }
    } else {
      onExpand(id, true);
    }
  }

  if (loading) {
    return (
      <a className={`${classPrefix}-tree__switcher`}>
        <Icon type="loading" />
      </a>
    );
  }

  let switcher = React.isValidElement<any>(switcherIcon) ? (
    switcherIcon
  ) : (
    <div>{switcherIcon}</div>
  );

  if (error) {
    switcher = <Icon type="error" />;
  }

  return enable ? (
    <a className={`${classPrefix}-tree__switcher`} onClick={handleExpand}>
      {switcher}
    </a>
  ) : (
    <div className={`${classPrefix}-tree__switcher`} />
  );
});

TreeNodeSwitcher.displayName = "TreeNodeSwitcher";
