import { Graph, Cell, Node, Edge } from "@antv/x6";
import { BorderSize, StructureType, TopicType } from "@/enum";
import {
  addTopic,
  getMindMapProjectByLocal,
  updateTopic,
} from "@/pages/mindmap/mindMap";
import { cellStyle, MindMapProjectInfo, TopicItem } from "@/types";
import { Dnd } from "@antv/x6-plugin-dnd";
import { selectTopic } from "@/utils/mindmapHander";
import { uuid } from "@/utils";
import { getTheme } from "@/pages/mindmap/theme";
import { traverseNode } from "@/utils/mindmapHander";

enum positionType {
  left = "left",
  right = "right",
  outside = "outside",
}
// 拖拽主题时指示器
let indicatorNode: Node | undefined;
let indicatorEdge: Edge | undefined;
// 插入的节点位置
let insertNode: Node | undefined;
let moveStart: { x: number; y: number } | undefined;
let dragging = false;
let currentShadowNode: Node | undefined;

export const bindMindMapEvents = (
  graph: Graph,
  mindProjectInfo?: MindMapProjectInfo,
  setMindProjectInfo?: (info: MindMapProjectInfo) => void,
  setSelectedCell?: (cell: Cell[]) => void,
  dndRef?: React.MutableRefObject<Dnd | undefined>
) => {
  graph.on("selection:changed", ({ selected }: { selected: Cell[] }) => {
    setSelectedCell?.(selected);
  });

  graph.on("node:mouseenter", (args) => {
    if (args.node.data?.type !== TopicType.main) {
      graph.disablePanning();
    }
  });

  graph.on("node:mouseleave", (args) => {
    graph.enablePanning();
  });

  /*********************************** 拖拽开始 *********************************/
  graph.on("node:mousedown", (args) => {
    moveStart = {
      x: args.x,
      y: args.y,
    };
  });

  graph.on("node:mousemove", (args) => {
    if (!args.node.data?.parentId || args.node.data?.shadow) {
      return;
    }

    if (
      !dragging &&
      moveStart &&
      (Math.abs(args.x - moveStart.x) > 5 || Math.abs(args.y - moveStart.y) > 5)
    ) {
      // 判断开始点是否有值，有值时当拖拽距离>5时开启影子节点
      dragging = true;
      const node = args.node;
      const shadowNode = graph.createNode({
        shape: "rect",
        width: node.size().width,
        height: node.size().height,
        x: args.x,
        y: args.y,
        label: node.data?.label,
        attrs: {
          body: {
            rx: node.data?.borderSize,
            ry: node.data?.borderSize,
            stroke: "#666",
            strokeWidth: 1,
          },
        },
        data: {
          ...node.data,
          shadow: true,
        },
      });
      setShadowMode(true, node, graph);
      currentShadowNode = shadowNode;
      dndRef?.current?.init();
      dndRef?.current?.start(shadowNode, args.e as unknown as MouseEvent);
    }
  });

  graph.on("node:mousemove", (args) => {
    // 节点拖拽-处理影子节点吸附问题
    if (currentShadowNode && setMindProjectInfo) {
      topicDragHander(graph, { x: args.x, y: args.y }, args.node);
    }
  });

  graph.on("node:mouseup", (args) => {
    // 拖拽结束
    if (indicatorNode && insertNode) {
      graph.removeCell(args.node.id + "-edge");
      setMindProjectInfo &&
        handleSwitchPosition(
          setMindProjectInfo,
          args.node.id,
          insertNode.id,
          undefined,
          graph
        );
    }

    // 成为自由节点
    if (
      currentShadowNode &&
      !indicatorNode &&
      args.node.data?.parentId &&
      canBeFreeNode(args.x, args.y, args.node)
    ) {
      setMindProjectInfo &&
        handleSwitchPosition(
          setMindProjectInfo,
          args.node.id,
          undefined,
          {
            x: args.x,
            y: args.y,
          },
          graph
        );
    }

    currentShadowNode && setShadowMode(false, args.node, graph);
    moveStart = undefined;
    dragging = false;
    currentShadowNode = undefined;
    dndRef?.current?.remove();
    if (indicatorEdge && indicatorNode) {
      graph.removeCells([indicatorEdge, indicatorNode]);
      indicatorEdge = undefined;
      indicatorNode = undefined;
    }
  });

  graph.on("node:move", (args) => {
    // 自由节点拖拽
    if (!args.node.data?.parentId) {
      setShadowMode(true, args.node, graph);
    }
  });

  graph.on("node:moving", (args) => {
    if (!args.node.data?.parentId && setMindProjectInfo) {
      setShadowMode(false, args.node, graph);
      topicDragHander(graph, { x: args.x, y: args.y }, args.node);
    }
  });

  graph.on("node:moved", (args) => {
    if (!args.node.data?.parentId) {
      setShadowMode(false, args.node, graph);
    }
  });
  /*********************************** 拖拽结束 *********************************/

  // 双击画布空白-新增自由主题
  graph.on("blank:dblclick", (args) => {
    if (setMindProjectInfo) {
      const topic = addTopic(TopicType.branch, setMindProjectInfo, undefined, {
        x: args.x,
        y: args.y,
        setMindProjectInfo,
        type: TopicType.branch,
        label: "自由主题",
        borderSize: BorderSize.medium,
      });

      selectTopic(graph, topic);
    }
  });

  /**
   * 节点数据更改
   */
  graph.on("node:change:*", (args) => {
    const { current, previous } = args;
    if (args.key === "data") {
      // 收折子项 setMindProjectInfo更新会重新渲染
      if (current.collapsed !== previous.collapsed) {
        setMindProjectInfo &&
          updateTopic(
            args.cell.id,
            { collapsed: current.collapsed },
            setMindProjectInfo
          );
        return;
      }
      if (current?.links && current.links.length !== previous?.links?.length) {
        setMindProjectInfo &&
          updateTopic(
            args.cell.id,
            { links: current.links },
            setMindProjectInfo
          );
      }
      // 本地缓存更新不会重新渲染
      updateTopic(args.cell.id, current, (info) => {
        localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
      });
    }
    if (args.key === "position") {
      if (args.cell.isNode() && !args.cell.data.parentId) {
        updateTopic(
          args.cell.id,
          { ...args.cell.data, x: current.x, y: current.y },
          (info) => {
            // localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
            setMindProjectInfo && setMindProjectInfo(info)
          }
        );
      }
    }
    if (args.key === "size") {
      updateTopic(
        args.cell.id,
        {
          width: current.width,
          height: current.height,
        },
        (info) => {
          localStorage.setItem("minMapProjectInfo", JSON.stringify(info));
        }
      );
    }
  });

  /**
   * 连接线更改
   */
  graph.on("edge:change:*", (args) => {
    if (args.key === "vertices" || args.key === "labels") {
      const link = args.edge.toJSON();
      const source = args.edge.getSourceCell();
      source?.setData({
        links: source.data?.links.map((item: Edge.Properties) => {
          if (item.id === link.id) return link;
          return item;
        }),
      });
    }
  });

  /**
   * 连接线删除
   */
  graph.on("edge:removed", (args) => {
    if (args.edge.data?.isLink) {
      // @ts-ignore
      const source = graph.getCellById(args.edge.source?.cell as string);
      source?.setData(
        {
          links: source.data?.links?.filter(
            (item: Edge.Properties) => item.id !== args.edge.id
          ),
        },
        {
          deep: false,
        }
      );
    }
  });
};

const canBeFreeNode = (x: number, y: number, node: Node): boolean => {
  if (!moveStart) return false;

  return Math.abs(x - moveStart.x) > 50 || Math.abs(y - moveStart.y) > 50;
};

// 判断当前点在主图的位置
const atNodePosition = (
  position: { x: number; y: number },
  x: number,
  y: number,
  width: number,
  height: number
): positionType => {
  if (
    position.x < x + width / 2 &&
    position.x > x &&
    position.y < y + height &&
    position.y > y
  ) {
    return positionType.left;
  }
  if (
    position.x < x + width &&
    position.x > x + width / 2 &&
    position.y < y + height &&
    position.y > y
  ) {
    return positionType.right;
  }
  return positionType.outside;
};

// 创建一个指示器
const addIndicator = (
  x: number,
  y: number,
  graph: Graph,
  targetNode: Node,
  atPosition: positionType
) => {
  if (indicatorEdge && indicatorNode) {
    graph.removeCells([indicatorEdge, indicatorNode]);
  }
  indicatorNode = graph.addNode({
    shape: "rect",
    width: 100,
    height: 26,
    x,
    y,
    attrs: {
      body: {
        fill: "#fecb99",
        stroke: "#fc7e00",
        strokeWidth: 1,
        rx: 2,
        ry: 2,
        style: {
          opacity: 0.6,
        },
      },
    },
  });

  indicatorEdge = graph.addEdge({
    source: {
      cell: targetNode.id,
      anchor: {
        name: atPosition === positionType.left ? "left" : "right",
        args: {
          dx: atPosition === positionType.left ? 3 : -3,
        },
      },
    },
    target: {
      cell: indicatorNode.id,
      anchor: {
        name: atPosition === positionType.left ? "right" : "left",
        args: {
          dx: atPosition === positionType.left ? -3 : 3,
        },
      },
    },
    connector: "normal",
    attrs: {
      line: {
        stroke: "#fc7e00",
        strokeWidth: 2,
        sourceMarker: {
          name: "",
        },
        targetMarker: {
          name: "",
        },
        style: {
          opacity: 0.6,
        },
      },
    },
  });
};

// 添加指示器
const setIndicator = (
  atPosition: positionType,
  targetNode: Node,
  graph: Graph
) => {
  switch (atPosition) {
    // 1、左侧位置
    case positionType.left: {
      const x = targetNode.position().x - 120;
      const y = targetNode.position().y + targetNode.size().height / 2 - 13;
      // 判断是左侧节点还是右侧节点
      if (targetNode.data?.parentId) {
        const parentNode = graph.getCellById(targetNode.data.parentId);
        // 左侧朝向的结构
        if (
          parentNode?.isNode() &&
          targetNode.position().x < parentNode.position().x
        ) {
          addIndicator(x, y, graph, targetNode, atPosition);
        }
      } else {
        const mindProjectInfo = getMindMapProjectByLocal();
        if (mindProjectInfo?.structure === StructureType.left) {
          addIndicator(x, y, graph, targetNode, atPosition);
        }
      }
      break;
    }
    // 2、右侧位置
    case positionType.right: {
      const x = targetNode.position().x + targetNode.size().width + 20;
      const y = targetNode.position().y + targetNode.size().height / 2 - 13;
      // 判断是左侧节点还是右侧节点
      if (targetNode.data?.parentId) {
        const parentNode = graph.getCellById(targetNode.data.parentId);
        // 右侧朝向的结构
        if (
          parentNode?.isNode() &&
          targetNode.position().x > parentNode.position().x
        ) {
          addIndicator(x, y, graph, targetNode, atPosition);
        }
      } else {
        const mindProjectInfo = getMindMapProjectByLocal();
        if (mindProjectInfo?.structure === StructureType.right) {
          addIndicator(x, y, graph, targetNode, atPosition);
        }
      }
      break;
    }
    // 外部位置
    case positionType.outside: {
    }
  }
};

// 判断目标节点是不是后代节点
const isDescendantNode = (targetNode: Node, originNode: Node, graph: Graph) => {
  if (targetNode.data?.parentId === originNode.id) {
    return true;
  }
  let isChild = false; // 是后代节点
  const findParent = (parentId: string) => {
    const cell = graph.getCellById(parentId);
    if (cell?.isNode() && cell.data.parentId) {
      if (cell.data.parentId === originNode.id) {
        isChild = true;
      } else {
        findParent(cell.data.parentId);
      }
    }
  };

  targetNode.data?.parentId && findParent(targetNode.data.parentId);

  return isChild;
};

/**
 * 主题拖拽放置
 * @param graph
 * @param setMindProjectInfo
 */
export const topicDragHander = (
  graph: Graph,
  position: { x: number; y: number },
  originNode: Node
) => {
  if (indicatorEdge) graph.removeCell(indicatorEdge);
  if (indicatorNode) graph.removeCell(indicatorNode);
  indicatorEdge = undefined;
  indicatorNode = undefined;
  // 1、 获取位置的地方附件是否有节点
  const nodes = graph.getNodes();
  nodes.forEach((targetNode) => {
    // 目标节点是自己、自己的后代、节点不做处理
    if (
      targetNode.id === originNode.id ||
      isDescendantNode(targetNode, originNode, graph)
    ) {
      return;
    }
    const { x, y } = targetNode.position();
    const { width, height } = targetNode.size();
    // 2、 找到是在节点哪个区域内
    const atPosition = atNodePosition(position, x, y, width, height);
    // 3、添加插入指示
    setIndicator(atPosition, targetNode, graph);
    // 4、 根据位置确定插入位置
    if ([positionType.left, positionType.right].includes(atPosition)) {
      insertNode = targetNode;
    }
  });
};

/**
 * 拖拽完毕，切换主题位置
 * @param setMindProjectInfo
 * @param sourceId 拖拽的节点
 * @param targetId 放置的节点
 * @param position 自由节点放置的位置
 * @returns
 */
const handleSwitchPosition = (
  setMindProjectInfo: (info: MindMapProjectInfo) => void,
  sourceId: string,
  targetId?: string,
  position?: { x: number; y: number },
  graph?: Graph
) => {
  const mindmapProjectInfo = getMindMapProjectByLocal();
  if (!mindmapProjectInfo) return;

  // 找到要拖拽的节点并删除
  let source: (TopicItem & cellStyle) | undefined;
  mindmapProjectInfo.topics.forEach((topic) => {
    if (topic.id === sourceId) {
      source = topic;
    }
    mindmapProjectInfo.topics = mindmapProjectInfo.topics.filter(
      (item) => item.id !== sourceId
    );
  });
  const topics = source
    ? mindmapProjectInfo.topics
    : traverseNode(mindmapProjectInfo.topics, (topic) => {
        const findItem = topic?.children?.find((item) => item.id === sourceId);
        if (findItem) {
          source = findItem;
          topic.children = topic.children?.filter(
            (item) => item.id !== sourceId
          );
        }
      });
  if (!source) return;

  // 处理节点样式
  const targetNode = targetId ? graph?.getCellById(targetId) : undefined;
  const theme = getTheme(
    mindmapProjectInfo.theme,
    targetNode?.isNode() && targetNode.data.type !== TopicType.main
      ? targetNode
      : undefined
  );

  source.type =
    targetNode?.data?.type === TopicType.main
      ? TopicType.branch
      : TopicType.sub;

  const themeObj = targetId ? theme[source.type] : theme[TopicType.branch];
  source.fill = {
    ...source.fill,
    ...themeObj.fill,
  };
  source.text = {
    ...source.text,
    ...themeObj.text,
  };
  source.stroke = {
    ...source.stroke,
    ...themeObj.stroke,
  };
  source.edge = {
    ...source.edge,
    ...themeObj.edge,
  };

  // 后代节点样式
  if (source.children)
    source.children = traverseNode(source.children, (topic) => {
      const theme = getTheme(
        mindmapProjectInfo.theme,
        graph?.createNode({
          data: {
            ...source,
          },
        })
      );
      topic.type = TopicType.sub;
      topic.fill = {
        ...topic.fill,
        ...theme[topic.type].fill,
      };
      topic.text = {
        ...topic.text,
        ...theme[topic.type].text,
      };
      topic.stroke = {
        ...topic.stroke,
        ...theme[topic.type].stroke,
      };
      topic.edge = {
        ...topic.edge,
        ...theme[topic.type].edge,
      };
    });

  if (targetId) {
    // 加入到目标节点下
    mindmapProjectInfo.topics = traverseNode(topics, (topic) => {
      if (topic.id === targetId) {
        if (!topic.children) {
          topic.children = [];
        }
        source &&
          topic.children.push({
            ...source,
            parentId: topic.id,
          });
      }
    });
  } else {
    const id = uuid();
    // 成为自由节点
    mindmapProjectInfo.topics.push({
      ...source,
      id,
      children: (source.children || []).map((item) => {
        item.parentId = id;
        return item;
      }),
      opacity: 100,
      x: position?.x,
      y: position?.y,
      type: TopicType.branch,
      parentId: null,
      links: (source.links || []).map((item) => {
        // 修改sourceId
        item.source = {
          ...item.source,
          id,
        };
        return item;
      }),
    });
  }

  setMindProjectInfo(mindmapProjectInfo);
};

/**
 * 设置影子模式-拖拽时变灰
 * @param node
 * @param graph
 */
export const setShadowMode = (enable: boolean, node: Node, graph: Graph) => {
  const data = node.getData();
  node.setData({
    opacity: enable ? 60 : 100,
  });

  if (data?.children?.length) {
    traverseNode(data.children, (topic) => {
      const cell = graph.getCellById(topic.id);
      const edge = graph.getCellById(topic.id + "-edge");
      if (cell && cell.isNode()) {
        cell.setData({
          opacity: enable ? 60 : 100,
        });
      }
      if (edge && edge.isEdge()) {
        edge.setAttrs({
          line: {
            style: {
              opacity: enable ? 0.6 : 1,
            },
          },
        });
      }
    });
  }
};
