import { PlusOutlined } from "@ant-design/icons";
import { Edge, EventArgs, Graph, Node } from "@antv/x6";
import React, { useEffect, useRef } from "react";
import { Dropdown, Popover } from "antd";
import NodeMenu from "../NodeMenu";

export default function Port(props: {
  hovered: boolean;
  out?: boolean;
  style?: React.CSSProperties;
  node?: Node;
  graph?: Graph;
  type?: "in" | "out" | "extra";
}) {
  const { hovered, style = {}, out = true, node, graph, type } = props;
  const [canAdd, setCanAdd] = React.useState(false);
  const [open, setOpen] = React.useState(false);
  const isDown = useRef(false);
  const isMove = useRef(false);
  const newEdge = React.useRef<Edge>();
  const params = new URL(document.location.href.replace('#', '')).searchParams;
  const mode = params.get("mode") ?? "edit";

  const extraStyle = React.useMemo(() => {
    if (out && canAdd) {
      return {
        transform: "scale(2) translateY(-50%)",
      };
    }
    if (!out) {
      return {
        width: 8,
        height: 16,
        borderRadius: 0,
        border: "none",
        left: -5,
      };
    } else {
      return {};
    }
  }, [canAdd]);

  const handleSetCanAdd = (value: boolean) => {
    if(mode === "ops") {
      setCanAdd(false);
      return;
    }
    out && setCanAdd(value);
    // TODO 与指针模式冲突
    graph?.togglePanning(!value);
  };

  // step1: 鼠标按下拖拽开始
  const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
    if(mode !== 'edit') return;

    isDown.current = true;
    node?.setData({ lock: true });
  };

  useEffect(() => {
    // step2: 移动鼠标添加连线
    // 检测移动 添加边 设置边目标位置
    graph?.on("node:mousemove", (args) => {
      if (isDown.current) {
        // 按下还没移动时创建边
        if (!isMove.current && node) {
          isMove.current = true;
          const ports = node.getPorts();
          const rightPort = ports.find((item) =>
            type ? item.group === "bottom" : item.group === "right"
          );
          newEdge.current = graph?.addEdge({
            source: { cell: node.id, port: rightPort?.id },
            router: {
              name: "manhattan",
              args: {
                padding: 20,
                excludeShapes: ["notice-node"],
              },
            },
            connector: { name: "rounded" },
            target: {
              x: args.x,
              y: args.y,
            },
            attrs: {
              line: {
                stroke: "#37d0ff",
                strokeWidth: 2,
              },
            },
          });
        } else {
          // 判断是否进入其他节点内
          // 查找到最顶层的节点 然后修改位置为节点
          const nodes = graph.getNodesInArea(args.x, args.y, 5, 5);
          const targetNode = nodes.length
            ? (nodes || [])?.reduce((prev, curr) => {
                const prevZ = prev.zIndex || -1;
                const currZ = curr?.zIndex || -1;
                return prevZ >= currZ ? prev : curr;
              })
            : null;

          // 进入节点 不存在已连接边 则添加一条边
          if (targetNode && targetNode.shape !== "notice-node") {
            const ports = targetNode.getPorts();
            const targetPort = ports.find((item) => item.group === "left");
            newEdge.current?.setTarget({
              cell: targetNode.id,
              port: targetPort?.id,
            });
          } else {
            // 否则修改边的终点
            newEdge.current?.setTarget({
              x: args.x,
              y: args.y,
            });
          }
        }
      }
    });

    // step3: 鼠标抬起，展示节点菜单或者连线连接到目标节点
    graph?.on("node:mouseup", (args) => {
      console.log("node:mouseup", newEdge.current);
      node?.setData({ lock: false });
      isDown.current = false;
      isMove.current = false;
      // 拖拽过程中释放鼠标 检测当前是否有节点
      if (newEdge.current) {
        // 判断是否连接到了节点
        if (!Object.hasOwn(newEdge.current.target, "x")) {
          newEdge.current.setAttrs({
            line: {
              stroke: "#7e8186",
            },
          });
          newEdge.current.setZIndex(0);
          newEdge.current = undefined;
          return;
        }
        // 判断是否存在menu-popover
        const els = document.querySelectorAll("[data-shape='menu-popover']");

        if (els.length) {
          return;
        }
        graph.addNode({
          shape: "menu-popover",
          x: args.x,
          y: args.y,
          data: {
            hideNodes: ['start-node'], // 隐藏节点
          },
        });
      }
    });

    // 添加节点完成设置连线目标节点
    graph?.on("node:change:addedNode", (args: EventArgs["cell:change:*"]) => {
      const { current } = args;
      if (newEdge.current && current) {
        const addNode = current?.addNode as Node;
        const ports = addNode.getPorts();
        const leftPort = ports?.find((item) => item.group === "left");
        const bottomPort = ports?.find((item) => item.group === "bottom");
        newEdge.current.setTarget({
          cell: addNode.id,
          port: type ? bottomPort?.id : leftPort?.id,
        });
        newEdge.current.setAttrs({
          line: {
            stroke: "#7e8186",
          },
        });
        newEdge.current = undefined;
      }
    });

    // 节点菜单menu-popver关闭, 如果连线目标没有节点信息移除连线
    graph?.on("node:change:closedPopover", () => {
      setTimeout(() => {
        if (Object.hasOwn(newEdge.current?.target || {}, "x")) {
          graph?.removeCells([newEdge.current!]);
          newEdge.current = undefined;
        }
      }, 300);
    });
  }, []);

  const { x, y } = node?.position() || { x: 0, y: 0 };
  const x1 = (node?.getBBox()?.width || 0) + x + 50;

  // 点击添加节点成功后，设置连线
  const handleAddChange = (addNode?: Node) => {
    setOpen(false);
    if (addNode && node) {
      const sourcePorts = node?.getPorts();
      const sourcePort = sourcePorts?.find((item) =>
        type ? item.group === "bottom" : item.group === "right"
      );
      const targetPorts = addNode?.getPorts();
      const targetPort = targetPorts?.find((item) => item.group === "left");

      graph?.addEdge({
        source: {
          cell: node.id,
          port: sourcePort?.id,
        },
        target: {
          cell: addNode.id,
          port: targetPort?.id,
        },
        router: {
          name: "manhattan",
          args: {
            padding: 20,
            excludeShapes: ["notice-node"],
          },
        },
        zIndex: 0,
        connector: { name: "rounded", args: {} },
        attrs: {
          line: {
            stroke: "#7e8186",
            strokeWidth: 2,
          },
        },
      });
    }
  };

  return (
    <Popover
      content={
        <NodeMenu
          graph={graph}
          onChange={handleAddChange}
          position={{ x: x1, y }}
        />
      }
      trigger={"click"}
      placement="right"
      arrow={false}
      open={open && mode === "edit"}
      onOpenChange={(open) => {
        setOpen(open);
      }}
    >
      {type === "extra" ? (
        <div
          className="node-port flex items-center justify-center"
          style={{
            transform: hovered
              ? "scale(1.2) translateY(-50%) rotate(45deg)"
              : "scale(1) translateY(-50%) rotate(45deg)",
            width: 16,
            height: 16,
            border: "none",
            borderRadius: 0,
            ...style,
          }}
          onMouseEnter={() => handleSetCanAdd(true)}
          onMouseLeave={() => handleSetCanAdd(false)}
          onMouseDown={handleMouseDown}
        >
          {out && (
            <PlusOutlined
              className="transform scale-0 transition duration-300 color-#fff text-8px"
              style={{ transform: canAdd ? "scale(1)" : "scale(0)" }}
            />
          )}
        </div>
      ) : (
        <div
          className="node-port flex items-center justify-center"
          style={{
            transform: hovered
              ? "scale(1.2) translateY(-50%)"
              : "scale(1) translateY(-50%)",
            ...style,
            ...extraStyle,
            cursor: mode === "edit" ? "crosshair" : "default",
          }}
          onMouseEnter={() => handleSetCanAdd(true)}
          onMouseLeave={() => handleSetCanAdd(false)}
          onMouseDown={handleMouseDown}
        >
          {out && (
            <PlusOutlined
              className="transform scale-0 transition duration-300 color-#fff text-8px"
              style={{ transform: canAdd ? "scale(1)" : "scale(0)" }}
            />
          )}
        </div>
      )}
    </Popover>
  );
}
