import { MarkerType, Position, useInternalNode, Node, Edge } from "@xyflow/react";
import { LayoutDirection, LayoutVisibility } from "./node";

/**
 * 获取流程图中的根节点
 * @param nodes - 所有节点数组
 * @param edges - 所有边的数组
 * @returns 根节点数组（没有入边的节点）
 */
export const getRootNodes = (nodes: Node[], edges: Edge[]): Node[] => {
  // 创建一个Set来存储所有有入边的节点ID
  const nodesWithIncoming = new Set(
    edges.map((edge) => edge.target)
  );

  // 过滤出没有入边的节点
  const rootNodes = nodes.filter(
    (node) => !nodesWithIncoming.has(node.id)
  );

  return rootNodes;
};
/**
 * 获取节点尺寸信息的工具函数
 * @param node 需要获取尺寸的节点对象
 * @param defaultSize 默认尺寸配置,包含默认宽度(150px)和高度(36px)
 * @returns 返回节点的尺寸信息对象,包含:
 *  - hasDimension: 是否已设置实际尺寸
 *  - width: 节点实际宽度
 *  - height: 节点实际高度
 *  - widthWithDefault: 实际宽度或默认宽度
 *  - heightWithDefault: 实际高度或默认高度
 */
export const getNodeSize = (
  node: Node,
  defaultSize = { width: 150, height: 36 }
) => {
  // 获取节点的实际宽高
  const nodeWith = node?.width;
  const nodeHeight = node?.height;
  // 检查节点是否同时设置了宽度和高度
  const hasDimension = [nodeWith, nodeHeight].every((e) => e != null);

  // 返回包含完整尺寸信息的对象
  // 使用空值合并运算符(??)在实际尺寸未设置时使用默认值
  return {
    hasDimension,
    width: nodeWith,
    height: nodeHeight,
    widthWithDefault: nodeWith ?? defaultSize.width,
    heightWithDefault: nodeHeight ?? defaultSize.height,
  };
};

export type IFixPosition = (pros: {
  x: number;
  y: number;
  width: number;
  height: number;
}) => {
  x: number;
  y: number;
};
/**
 * 节点布局计算函数
 * @description 根据给定的节点信息和布局参数,计算节点的最终布局属性
 * @param props 布局参数对象
 * @param props.node 需要布局的节点对象
 * @param props.position 节点的初始位置坐标
 * @param props.direction 布局方向,'horizontal'表示水平布局,'vertical'表示垂直布局
 * @param props.visibility 节点可见性,'visible'表示可见,其他值表示隐藏
 * @param props.fixPosition 可选的位置修正函数,用于调整最终位置
 * @returns 返回计算好布局属性的节点对象
 */
export const getNodeLayouted = (props: {
  node: Node;
  position: { x: number; y: number };
  direction: LayoutDirection;
  visibility: LayoutVisibility;
  fixPosition?: IFixPosition;
}) => {
  // 解构布局参数,设置位置修正函数的默认值
  const {
    node,
    position,
    direction,
    visibility,
    fixPosition = (p) => ({ x: p.x, y: p.y }),
  } = props;

  // 计算节点的显示状态和布局方向
  const hidden = visibility !== "visible";
  const isHorizontal = direction === "horizontal";

  // 获取节点尺寸信息
  const { width, height, widthWithDefault, heightWithDefault } =
    getNodeSize(node);

  // 根据布局方向设置节点的连接点位置
  node.targetPosition = isHorizontal ? Position.Left : Position.Top;
  node.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;

  // 返回带有完整布局属性的节点对象
  return {
    ...node,
    width,
    height,
    hidden,
    position: fixPosition({
      ...position,
      width: widthWithDefault,
      height: heightWithDefault,
    }),
    style: {
      ...node.style,
      opacity: hidden ? 0 : 1,
    },
  };
};

/**
 * 边布局计算函数
 * @description 根据给定的边信息和可见性参数,计算边的最终布局属性
 * @param props 布局参数对象
 * @param props.edge 需要布局的边对象
 * @param props.visibility 边的可见性,'visible'表示可见,其他值表示隐藏
 * @returns 返回计算好布局属性的边对象
 */
export const getEdgeLayouted = (props: {
  edge: Edge;
  visibility: LayoutVisibility;
}) => {
  const { edge, visibility } = props;
  const hidden = visibility !== "visible";

  // 返回带有完整布局属性的边对象
  return {
    ...edge,
    hidden,
    markerEnd: {
      type: MarkerType.ArrowClosed, // 设置箭头样式为闭合箭头
    },
    style: {
      ...edge.style,
      opacity: hidden ? 0 : 1,
    },
  };
};