import React, {
  useState,
  useImperativeHandle,
  forwardRef,
  useRef,
  useEffect,
  useMemo,
  useContext,
  memo,
} from "react";
import classNames from "classnames";
import { TreeContext, TreeContextValue } from "./TreeContext";
import { Check } from "../check";
import { Icon } from "../icon";
import { injectValue } from "../_util/inject-value";
import { useConfig } from "../_util/config-context";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { TreeNodeProps, TreeNodeEventData } from "./TreeProps";
import { mergeEventProps } from "../_util/merge-event-props";
import { TreeNode as DeprecatedTreeNode } from "./deprecated/TreeNode";

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 TreeNodeInnerProps {
  /**
   * 深度
   */
  level?: number;

  /**
   * 包含完整路径的 id 串
   */
  pathKey?: string;
}

export const TreeNode = forwardRefWithStatics(
  function TreeNode(props: TreeNodeProps, ref: React.Ref<HTMLDivElement>) {
    const {
      flatten,
      activable,
      onActive,
      expandedIds,
      fullExpandable,
      fullActivable,
      onExpand,
      onLoad,
      onLoadError,
      switcherIcon,
      selectable: treeSelectable,
    } = useContext(TreeContext);

    const expanded = useMemo(() => expandedIds.includes(props.id), [
      expandedIds,
      props.id,
    ]);

    if (!flatten) {
      return <DeprecatedTreeNode {...props} ref={ref} />;
    }

    return (
      <TreeNodeInner
        {...props}
        ref={ref}
        activable={activable}
        onActive={onActive}
        fullExpandable={fullExpandable}
        fullActivable={fullActivable}
        expanded={expanded}
        onExpand={onExpand}
        onLoad={onLoad}
        onLoadError={onLoadError}
        switcherIcon={switcherIcon}
        treeSelectable={treeSelectable}
      />
    );
  },
  {
    ActionLink: TreeActionLink,
  }
);

TreeNode.displayName = "TreeNode";

const TreeNodeInner = memo(
  forwardRef(function TreeNodeInner(
    allProps: TreeNodeProps &
      TreeNodeInnerProps &
      Pick<
        TreeContextValue,
        | "activable"
        | "onActive"
        | "fullExpandable"
        | "fullActivable"
        | "onExpand"
        | "onLoad"
        | "onLoadError"
        | "switcherIcon"
      > & { expanded?: boolean; treeSelectable?: boolean },
    ref: React.Ref<HTMLDivElement>
  ) {
    const {
      id,
      level = 0,
      content,
      icon,
      operation,
      expandable,
      children,
      selectable: nodeSelectable,
      // Context
      activable,
      fullActivable,
      onActive,
      fullExpandable,
      expanded,
      onExpand,
      onLoad,
      onLoadError,
      switcherIcon,
      treeSelectable,
      // 外层使用
      disableSelect,
      pure,
      className,
      style,
      // 节点内无用
      height,
      pathKey,
      // 可透传属性
      ...props
    } = allProps;

    const { classPrefix } = useConfig();

    const selectable =
      typeof nodeSelectable !== "undefined"
        ? nodeSelectable && treeSelectable
        : treeSelectable;

    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" content={content} />
          <span
            className={`${classPrefix}-tree__label-title`}
            onClick={event => {
              if (!fullActivable) {
                onActive(id, event, allProps);
              }
            }}
          >
            {nodeIcon}
            {content}
            {operation && (
              <div
                className={`${classPrefix}-tree__action`}
                onClick={e => e.stopPropagation()}
              >
                {operation}
              </div>
            )}
          </span>
        </div>
      ) : (
        <div
          className={`${classPrefix}-tree__label`}
          onClick={event => {
            if (!fullActivable && activable) {
              onActive(id, event, allProps);
            }
          }}
        >
          {selectable ? (
            <Check name={id} content={content} type="checkbox">
              {nodeIcon}
              {content}
            </Check>
          ) : (
            <>
              {nodeIcon}
              <span className={`${classPrefix}-tree__label-title`}>
                {content}
              </span>
            </>
          )}
          {operation && (
            <div
              className={`${classPrefix}-tree__action`}
              onClick={e => e.stopPropagation()}
            >
              {operation}
            </div>
          )}
        </div>
      );

    return (
      <div
        ref={ref}
        className={`${classPrefix}-tree__node-content`}
        style={
          fullActivable || fullExpandable ? { cursor: "pointer" } : undefined
        }
        // eslint-disable-next-line dot-notation
        draggable={props["draggable"]}
        {...mergeEventProps(props, {
          onClick: event => {
            if (fullActivable && activable) {
              onActive(id, event, allProps);
            }
            if (fullExpandable && Boolean(expandable || children)) {
              handleExpand(event);
            }
          },
        })}
      >
        {new Array(level).fill(null).map((_, i) => (
          <span key={i} className={`${classPrefix}-tree__space`}></span>
        ))}
        <TreeNodeSwitcher
          id={id}
          ref={switcherRef}
          icon={switcherIcon}
          enable={Boolean(expandable || children)}
          expanded={expanded}
          onExpand={onExpand}
          onLoad={onLoad}
          onLoadError={onLoadError}
          classPrefix={classPrefix}
          allProps={allProps}
        />
        {nodeContent}
      </div>
    );
  })
);

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;
  allProps: TreeNodeEventData;
}

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,
    allProps,
  }: 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, event, allProps);
      return;
    }

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

  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";
