import * as React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
// import { withStyles } from "@material-ui/styles";
//@ts-ignore
import { TreeContext, TreeContextProps } from "./context-types";
import { getDataAndAria } from "./util";
import { IconType, Key, DataNode, NodeInstance } from "./interface";
import { Indent } from "./indent";
import { convertNodePropsToEventData } from "./util/tree-util";

/**
 * Some basic component styles
 */

// const styles = {
//   coTree: {
//     margin: 0,
//     border: "1px solid transparent"
//   },
//   coTreeTreenode: {
//     margin: 0,
//     padding: 0,
//     height: "29px",
//     lineHeight: "20px",
//     outline: 0,
//     whiteSpace: "nowrap",
//     listStyle: "none",
//     display: "flex",
//     alignItems: "center",
//     userSelect: "none",
//     borderTop: "2px solid transparent",
//     borderBottom: "2px solid transparent",
//     "& :hover": {
//       backgroundColor: "#eee"
//     },
//     dragOver: {
//       color: "white",
//       backgroundColor: "#99bcf5",
//       opacity: 0.7
//     },
//     dragOverGapTop: {
//       borderTopColor: "skyblue"
//     },
//     dragOverGapBottom: {
//       borderBottomColor: "skyblue"
//     },
//     draggable: {
//       color: "#333",
//       userSelect: "none"
//     }
//   },
//   coTreeNodeContentWrapper: {
//     display: "inline-block",
//     height: "20px",
//     margin: 0,
//     padding: "1px 2px 0 2px",
//     textDecoration: "none",
//     verticalAlign: "top",
//     cursor: "pointer"
//   },
//   coTreeIconEle: {
//     display: "inline-block",
//     width: "16px",
//     height: "16px",
//     lineHeight: "16px",
//     verticalAlign: "middle",
//     border: "none",
//     outline: "none",
//     cursor: "pointer"
//   },
//   coTreeSwitcher: {
//     display: "inline-block",
//     width: "16px",
//     height: "16px",
//     lineHeight: "16px",
//     verticalAlign: "middle",
//     border: "none",
//     outline: "none",
//     cursor: "pointer",
//     marginLeft: "20px"
//   },
//   coTreeSwitcher_open: {
//     background: `url(${"./assets/arrowright.png"}) no-repeat 0 0 transparent`,
//     transform: "rotate(90deg)",
//     transition: "0.2s linear"
//   },
//   coTreeSwitcher_close: {
//     background: `url(${"./assets/arrowright.png"}) no-repeat 0 0 transparent`,
//     transition: "0.2s linear"
//   },
//   coTreeTreeNodeDisabled: {
//     span: {
//       "&:not(coTreeSwitcher)": {
//         color: "#767676",
//         cursor: "not-allowed"
//       }
//     },
//     a: {
//       color: "#767676",
//       cursor: "not-allowed",
//       span: {
//         color: "#767676",
//         cursor: "not-allowed"
//       }
//     }
//   },
//   coTreeTreenodeActive: {
//     background: "rgba(19, 18, 18, 0.1)"
//   },
//   coTreeTreenodeSelected: {
//     backgroundColor: "#e6ecf1",
//     fontWieght: "bold",
//     opacity: "0.8"
//   },
//   coTreeIcon__open: {
//     marginRight: "2px",
//     marginBottom: "4px",
//     verticalAlign: "middle",
//     background: `url(${"./assets/notebookp.png"}) no-repeat 0 0 transparent`
//   },
//   coTreeIcon__close: {
//     marginRight: "2px",
//     marginBottom: "4px",
//     verticalAlign: "middle",
//     background: `url(${"./assets/notebookp.png"}) no-repeat 0 0 transparent`
//   },
//   coTreeIcon__docu: {
//     marginRight: "2px",
//     marginBottom: "4px",
//     verticalAlign: "middle",
//     background: `url(${"./assets/notebookc.png"}) no-repeat 0 0 transparent`
//   },
//   coTreeIcon__customize: {
//     marginRight: "2px",
//     verticalAlign: "top"
//   },
//   coTreeIndentUnit: {
//     display: "inline-block",
//     paddingLeft: "10px"
//   }
// };

const ICON_OPEN = "open";
const ICON_CLOSE = "close";

const defaultTitle = "---";

interface TreeNodeProps {
  eventKey?: Key;
  prefixCls?: string;
  className?: string;
  style?: React.CSSProperties;

  expanded?: boolean;
  selected?: boolean;
  loaded?: boolean;
  loading?: boolean;
  title?: React.ReactNode | ((data: DataNode) => React.ReactNode);
  dragOver?: boolean;
  dragOverGapTop?: boolean;
  dragOverGapBottom?: boolean;
  pos?: string;
  domRef?: React.Ref<HTMLDivElement>;
  selectHandle?: React.Ref<HTMLSpanElement>;
  //For easy data access

  data?: DataNode;
  isStart?: boolean[];
  isEnd?: boolean[];
  active: boolean;
  onMouseMove?: React.MouseEventHandler<HTMLDivElement>;

  isTreeNode?: boolean; //Old type isLeaf
  selectable?: boolean;
  disabled?: boolean;
  icon?: IconType;
  switcherIcon?: IconType;
  children?: React.ReactNode;
  onSelect?: (...args: any[]) => any;

  node: NodeInstance;
}

interface InternalTreeNodeProps extends TreeNodeProps {
  context?: TreeContextProps;
}

const InternalTreeNode: React.FC<InternalTreeNodeProps> = props => {
  InternalTreeNode.propTypes = {
    prefixCls: PropTypes.string,
    className: PropTypes.string,
    style: PropTypes.object,
    onSelect: PropTypes.func,

    eventKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    expanded: PropTypes.bool,
    selected: PropTypes.bool,
    loaded: PropTypes.bool,
    loading: PropTypes.bool,
    title: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
    dragOver: PropTypes.bool,
    dragOverGapTop: PropTypes.bool,
    dragOverGapBottom: PropTypes.bool,
    pos: PropTypes.string,

    isTreeNode: PropTypes.bool,
    selectable: PropTypes.bool,
    disabled: PropTypes.bool,
    icon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
    switcherIcon: PropTypes.oneOfType([PropTypes.node, PropTypes.func])
  };

  //   const [state, setState] = React.useState({
  //     dragNodeHighlight: false
  //   });
  const [dragNodeHighlight, setDragNodeHighlight] = React.useState<boolean>(
    false
  );
  const domRef = React.useRef<HTMLDivElement>();

  const selectHandle = React.useRef<HTMLSpanElement>();

  // let selectHandle: HTMLSpanElement;

  React.useEffect(() => {
    syncLoadData(props);
  });

  const onSelectorClick = e => {
    //Click trigger before select operation
    const {
      context: { onNodeClick }
    } = props;
    onNodeClick(e, convertNodePropsToEventData(props));
    if (isSelectable()) {
      onSelect(e);
    }
  };

  const onSelectorDoubleClick = e => {
    const {
      context: { onNodeDoubleClick }
    } = props;
    onNodeDoubleClick(e, convertNodePropsToEventData(props));
  };

  const onSelect = e => {
    if (isDisabled()) return;

    const {
      context: { onNodeSelect }
    } = props;
    e.preventDefault();
    onNodeSelect(e, convertNodePropsToEventData(props));
  };

  const onMouseEnter = e => {
    const {
      context: { onNodeMouseEnter }
    } = props;
    onNodeMouseEnter(e, convertNodePropsToEventData(props));
  };

  const onMouseLeave = e => {
    const {
      context: { onNodeMouseLeave }
    } = props;
    onNodeMouseLeave(e, convertNodePropsToEventData(props));
  };

  const onContextMenu = e => {
    const {
      context: { onNodeContextMenu }
    } = props;
    onNodeContextMenu(e, convertNodePropsToEventData(props));
  };

  const onDragStart = e => {
    const {
      context: { onNodeDragStart }
    } = props;

    e.stopPropagation();
    setDragNodeHighlight(false);
    onNodeDragStart(e, props.node);

    try {
      // ie throw error
      // firefox-need-it
      e.dataTransfer.setData("text/plain", "");
    } catch (error) {
      // empty
    }
  };

  const onDragEnter = e => {
    const {
      context: { onNodeDragEnter }
    } = props;

    e.stopPropagation();
    onNodeDragEnter(e, props.node);
  };

  const onDragOver = e => {
    const {
      context: { onNodeDragOver }
    } = props;

    e.preventDefault();
    e.stopPropagation();
    onNodeDragOver(e, props.node);
  };

  const onDragLeave = e => {
    const {
      context: { onNodeDragLeave }
    } = props;

    e.stopPropagation();
    onNodeDragLeave(e, props.node);
  };

  const onDragEnd = e => {
    const {
      context: { onNodeDragEnd }
    } = props;

    e.stopPropagation();
    setDragNodeHighlight(false);
    onNodeDragEnd(e, props.node);
  };

  const onDrop = e => {
    const {
      context: { onNodeDrop }
    } = props;

    e.preventDefault();
    e.stopPropagation();
    setDragNodeHighlight(false);
    onNodeDrop(e, props.node);
  };

  //Disable item still can be switch
  const onExpand = e => {
    const {
      context: { onNodeExpand }
    } = props;
    onNodeExpand(e, convertNodePropsToEventData(props));
  };

  // const setSelectHandle = node => {
  //   // (selectHandle: HTMLSpanElement) => selectHandle;
  //   selectHandle = node;
  //   return selectHandle;
  // };

  const getNodeState = () => {
    const { expanded } = props;
    if (isLeaf()) return null;
    return expanded ? ICON_OPEN : ICON_CLOSE;
  };

  const hasChildren = () => {
    const { eventKey } = props;
    const {
      context: { keyEntities }
    } = props;
    const { children } = keyEntities[eventKey] || {};

    return !!(children || []).length;
  };

  const isLeaf = () => {
    const { isTreeNode, loaded } = props;
    const {
      context: { loadData }
    } = props;
    const getChildren = hasChildren();

    if (isTreeNode === false) {
      return false;
    }
    return (
      isTreeNode ||
      (!loadData && !getChildren) ||
      (loadData && loaded && !getChildren)
    );
  };

  const isDisabled = () => {
    const { disabled } = props;
    const {
      context: { disabled: treeDisabled }
    } = props;

    return !!(treeDisabled || disabled);
  };

  //Load data to avoid default expanded tree without data

  const syncLoadData = prop => {
    const { expanded, loading, loaded } = prop;
    const {
      context: { loadData, onNodeLoad }
    } = props;
    if (loading) return;

    //Read from state to avoid loadData at same time
    if (loadData && expanded && !isLeaf()) {
      //We needn't reload data when has children in sync logic
      //It's only needed in node expanded
      if (!hasChildren() && !loaded) {
        onNodeLoad(convertNodePropsToEventData(props));
      }
    }
  };

  const isSelectable = () => {
    const { selectable } = props;
    const {
      context: { selectable: treeSelectable }
    } = props;

    //Ignore when selectable is undefined or null
    if (typeof selectable === "boolean") {
      return selectable;
    }
    return treeSelectable;
  };

  //Switcher
  const renderSwitcher = () => {
    const { expanded, switcherIcon: switcherIconFromProps } = props;
    const {
      context: { prefixCls, switcherIcon: switcherIconFromCtx }
    } = props;
    const switcherIcon = switcherIconFromProps || switcherIconFromCtx;

    if (isLeaf()) {
      return (
        <span
          className={classNames(
            `${prefixCls}-switcher`,
            `${prefixCls}-switcher-noop`
          )}
        >
          {typeof switcherIcon === "function"
            ? switcherIcon({ ...props, isTreeNode: true })
            : switcherIcon}
        </span>
      );
    }

    const switcherCls = classNames(
      `${prefixCls}-switcher`,
      `${prefixCls}-switcher_${expanded ? ICON_OPEN : ICON_CLOSE}`
    );
    return (
      <span onClick={onExpand} className={switcherCls}>
        {typeof switcherIcon === "function"
          ? switcherIcon({ ...props, isTreeNode: false })
          : switcherIcon}
      </span>
    );
  };

  const renderIcon = () => {
    const { loading } = props;
    const {
      context: { prefixCls }
    } = props;

    return (
      <span
        className={classNames(
          `${prefixCls}-iconEle`,
          `${prefixCls}-icon__${getNodeState() || "docu"}`,
          loading && `${prefixCls}-icon_loading`
        )}
      />
    );
  };

  //Icon + Title

  const renderSelector = () => {
    const dragNodeHighlights = dragNodeHighlight;
    const { title, selected, icon, loading, data } = props;
    const {
      context: { prefixCls, showIcon, icon: treeIcon, draggable, loadData }
    } = props;
    const disabled = isDisabled();

    const wrapClass = `${prefixCls}-node-content-wrapper`;

    //Icon - still show loading icon when loading without showIcon

    let $icon;

    if (showIcon) {
      const currentIcon = icon || treeIcon;

      $icon = currentIcon ? (
        <span
          className={classNames(
            `${prefixCls}-iconEle`,
            `${prefixCls}-icon__customize`
          )}
        >
          {typeof currentIcon === "function" ? currentIcon(props) : currentIcon}
        </span>
      ) : (
        renderIcon()
      );
    } else if (loadData && loading) {
      $icon = renderIcon();
    }

    //Title
    const $title = (
      <span className={`${prefixCls}-title`}>
        {typeof title === "function" ? title(data) : title}
      </span>
    );

    return (
      <span
        ref={selectHandle}
        title={typeof title === "string" ? title : ""}
        className={classNames(
          `${wrapClass}`,
          `${wrapClass}-${getNodeState() || "normal"}`,
          !disabled &&
            (selected || dragNodeHighlights) &&
            `${prefixCls}-node-selected`,
          !disabled && draggable && "draggable"
        )}
        draggable={(!disabled && draggable) || undefined}
        aria-grabbed={(!disabled && draggable) || undefined}
        onMouseEnter={onMouseEnter}
        onMouseLeave={onMouseLeave}
        onContextMenu={onContextMenu}
        onClick={onSelectorClick}
        onDoubleClick={onSelectorDoubleClick}
        onDragStart={draggable ? onDragStart : undefined}
      >
        {$icon}
        {$title}
      </span>
    );
  };

  const {
    eventKey,
    className,
    style,
    dragOver,
    dragOverGapTop,
    dragOverGapBottom,
    isTreeNode,
    isStart,
    isEnd,
    expanded,
    selected,
    loading,
    // domRef,
    active,
    onMouseMove,
    ...otherProps
  } = props;
  const {
    context: { prefixCls, filterTreeNode, draggable, keyEntities }
  } = props;
  const disabled = isDisabled();
  const dataOrAriaAttributeProps = getDataAndAria(otherProps);
  const { level } = keyEntities[eventKey] || {};

  return (
    <div
      ref={domRef}
      className={classNames(className, `${prefixCls}-treeNode`, {
        [`${prefixCls}-treenode-disabled`]: disabled,
        [`${prefixCls}-treenode-switcher-${
          expanded ? "open" : "close"
        }`]: !isTreeNode,
        [`${prefixCls}-treenode-selected`]: selected,
        [`${prefixCls}-treenode-loading`]: loading,
        [`${prefixCls}-treenode-active`]: active,

        "drag-over": !disabled && dragOver,
        "drag-over-gap-top": !disabled && dragOverGapTop,
        "drag-over-gap-bottom": !disabled && dragOverGapBottom,
        "filter-node":
          filterTreeNode && filterTreeNode(convertNodePropsToEventData(props))
      })}
      style={style}
      onDragEnter={draggable ? onDragEnter : undefined}
      onDragOver={draggable ? onDragOver : undefined}
      onDragLeave={draggable ? onDragLeave : undefined}
      onDrop={draggable ? onDrop : undefined}
      onDragEnd={draggable ? onDragEnd : undefined}
      onMouseMove={onMouseMove}
      {...dataOrAriaAttributeProps}
    >
      <Indent
        prefixCls={prefixCls}
        level={level}
        isStart={isStart}
        isEnd={isEnd}
      />
      {renderSwitcher()}
      {renderSelector()}
    </div>
  );
};

const ContextTreeNode: React.FC<TreeNodeProps> = props => {
  const context = React.useContext(TreeContext);

  return <InternalTreeNode {...props} context={context} />;
};

ContextTreeNode.displayName = "TreeNode";

ContextTreeNode.defaultProps = {
  title: defaultTitle
};

(ContextTreeNode as any).isTreeNode = 1;

export { TreeNodeProps, InternalTreeNode, InternalTreeNodeProps };
export default ContextTreeNode;
