// 节点管理器
import { isPanel, isGroup, isComponent, isRefScreen, deepFindOne } from 'utils/utils';
import { getId } from 'utils/interaction';
import { screenEditModel } from 'models/screen-edit';
import defaultActions from 'constants/default-actions';
import chunk from 'lodash/chunk';
import { Nodes, Edges } from 'models/node-types';
import max from 'lodash/max';
import { State } from 'containers/nodes-edit/index';
import { ComponentsById } from 'models/component.types';
import { ByIdType, Colors, EventList, FindNod, LayersType, Type } from './nodes.types';

const defaultTriggers = {
  name: '当数据接口请求完成时',
  value: 'dataChange',
};
let timeOut: any = null,
  componentMap = {};
export function resetNodeCardId(id) {
  id = (id + '').replace(/(.*)Card-/, ''); // 移除特殊id
  const numberId = Number(id); // string 转 number
  return isNaN(numberId) ? id : numberId;
}

export function nodesDataTransform({
  componentsById,
  id,
  panelsById,
  screensById,
  constants = [],
  tenantConstants = [],
}: {
  componentsById: ByIdType;
  id: number;
  panelsById: ByIdType;
  screensById: ByIdType;
  constants: any[];
  tenantConstants: any[];
}) {
  // 数据转换
  const screen = screensById[id]; // 首屏
  if (screen && componentsById) {
    const { layers, id } = screen;

    const newLayers = deepTree({
      layers: layers,
      componentsById,
      screenId: id,
      panelsById,
      constants: constants.concat(
        tenantConstants.map((d) => {
          return {
            ...d,
            category: '我的',
          };
        }),
      ),
      screensById,
      allLayers: layers,
    });
    componentMap = {};
    return newLayers;
  }
  return [];
}

export function getLayer({ id, screensById, allLayers }) {
  let layer = deepFindOne(
    allLayers,
    (item) => {
      return item.id == id;
    },
    'layers',
  );
  if (!layer) {
    let result;
    for (const screenId in screensById) {
      const screen = screensById[screenId];
      const { layers } = screen;
      if (layers) {
        result = deepFindOne(
          layers,
          (item) => {
            return item.id == id;
          },
          'components',
        );
        if (result) {
          break;
        }
      }
    }
    layer = result && {
      ...result,
      deepType: 'events',
    };
  }
  if (layer) {
    return layer;
  }
}

export function isParentTopGroup({ component, screensById }) {
  // 查询当前组件是否属于某组/动态面板/状态等
  if (!component) return false;
  const { screenId, id } = component;
  const screen = screensById[screenId];
  if (!screen) return false;
  const rootIds = screen?.layers.map((d) => d.id);
  // 只要目标组件不在根节点，那么一定属于某组
  return !rootIds.includes(id);
}

export function deepTree({
  layers,
  componentsById,
  parentGroupId,
  constants,
  panelsById,
  screenId,
  screensById,
  allLayers,
}: {
  parentGroupId?: number | string;
  layers: any[];
  screenId: number;
  componentsById: ByIdType;
  constants: any[];
  panelsById: ByIdType;
  screensById: ByIdType;
  allLayers: [];
}) {
  // 一维数组处理成多维数组，处理成树状结构
  if (isLengthArray(layers)) {
    // 图层顺序倒叙
    return [...layers]
      .reverse()
      .filter((item) => {
        if (item._state === 'bind') {
          return true;
        } else if (['children', 'group'].includes(item._state)) {
          return isBeLinked(item.id, componentsById);
        }
        const component = item.id && componentsById[item.id];
        if (component) {
          const componentBind = getActionsComponents(component);

          return isLengthArray(componentBind) || isLengthArray(component.children);
        }
        return true;
      })
      .map((item) => {
        if (componentMap[item.id]) {
          return;
        }

        componentMap[item.id] = item;

        if (isGroup(item.id)) {
          //组
          const { components, id, name, show } = item;
          const category = '组';
          const layers = deepTree({
            layers: components.map((d) => ({
              ...d,
              _state: isComponent(d.id) && 'group',
            })),
            screenId,
            componentsById,
            constants,
            parentGroupId: id,
            panelsById,
            screensById,
            allLayers,
          });

          // 维护componentMap
          // layers.map((d) => (componentMap[d.id] = d));

          return {
            id,
            name,
            show,
            screenId,
            parentGroupId,
            color: Colors[category],
            moduleName: category,
            type: LayersType.GROUP,
            layers,
          };
        } else if (isPanel(item.id)) {
          //  动态面板
          const category = '动态面板';
          const panelId = getId(item.id);
          const panel = panelsById[panelId];
          if (panel) {
            const { states = [] } = panel;
            const panelLayers = states
              .map((state) => {
                const screen = screensById[state];
                if (screen) {
                  const { id, name, layers } = screen;
                  const stateLayers = deepTree({
                    layers,
                    componentsById,
                    parentGroupId: id,
                    screenId: id,
                    constants,
                    panelsById,
                    screensById,
                    allLayers,
                  });
                  return {
                    id: id,
                    name,
                    screenId,
                    parentGroupId: item.id,
                    color: Colors[category],
                    type: LayersType.STATE,
                    layers: stateLayers,
                  };
                }
              })
              .filter((d) => d);

            return {
              id: item.id,
              name: panel.name,
              moduleName: category,
              parentGroupId,
              color: Colors[category],
              type: LayersType.PANEL,
              screenId: panel.screenId,
              layers: panelLayers,
            };
          }
          return [];
        } else if (isRefScreen(item.id)) {
          const category = '引用面板';
          componentMap[item.id] = item;
          // 引用面板只有被自定义事件使用才有用
          return {
            ...item,
            parentGroupId,
            screenId,
            moduleName: category,
            color: Colors[category],
            type: LayersType.REF,
          };
        }

        const {
          category = '默认',
          name: categoryName,
          thumb,
        } = constants.find((d) => d.moduleName === item.moduleName) || {};
        const color = Colors[category];
        if (isComponent(item.id)) {
          // 组件
          const component = componentsById[item.id];
          const { children = [], name, screenId } = component;

          const events = getActionsComponents(component);
          const actionsComponentIds = getComponentBind(component);
          const actionLayers = actionsComponentIds
            .map((id) =>
              getLayer({
                id,
                screensById,
                allLayers,
              }),
            )
            .filter((d) => {
              if (d) {
                return !(
                  componentMap[d.id] ||
                  isParentTopGroup({
                    component: componentsById[d.id],
                    screensById,
                  })
                );
              }
              return d;
            })
            .map((d) => {
              return {
                ...d,
                _state: 'bind',
              };
            });

          const childrenLayers = children
            .filter((id) => isBeLinked(id, componentsById))
            .map((id) => {
              const childComponent = componentsById[id];
              return {
                hideDefault: false,
                id,
                screenId,
                parentId: component.id,
                lock: false,
                moduleName: categoryName || childComponent.base.module_name,
                name: childComponent.name,
                show: true,
                _state: 'children',
              };
            });
          const actionsComponents = deepTree({
            layers: actionLayers,
            componentsById,
            constants,
            screenId,
            panelsById,
            screensById,
            allLayers,
          });

          const componentGroupId = `group_${component.id}`;
          const childrenComponents = deepTree({
            layers: childrenLayers,
            componentsById,
            constants,
            screenId,
            panelsById,
            screensById,
            allLayers,
          });

          const componetresult = {
            ...item,
            color,
            parentGroupId,
            screenId,
            moduleName: categoryName || item.moduleName,
            thumb,
            layers: actionsComponents,
            events,
            type: LayersType.COMPONENT,
          };

          if (isLengthArray(childrenComponents)) {
            const componentGroup = {
              id: componentGroupId,
              name,
              show: true,
              screenId,
              parentGroupId,
              color: Colors[category],
              moduleName: category,
              type: LayersType.GROUP,
              layers: childrenComponents.map((d) => ({ ...d, parentGroupId: componentGroupId })),
            };
            return {
              ...componetresult,
              children: childrenLayers.map((d) => d.id),
              layers: componetresult.layers.concat(componentGroup),
            };
          }
          return componetresult;
        }

        // 未知
        if (item.id) {
          return {
            ...item,
            color,
            parentGroupId,
            moduleName: categoryName || item.moduleName,
            type: LayersType.UNKNOWN,
          };
        }
      })
      .filter((d) => {
        return d;
      });
  }

  return [];
}

export function getActionsComponents(component) {
  // 获取组件身上绑定的自定义事件/动作 上绑定的组件
  const events = component?.events;
  if (isLengthArray(events)) {
    return events.reduce((all, item) => {
      if (item && item.id && isLengthArray(item?.actions)) {
        // 说明数据结构安全
        const actions = item.actions
          .filter((d) => d && d.id)
          .map((d) => {
            return {
              action: d.action,
              component: d.component,
              id: d.id,
              name: d.name,
              componentScope: d.componentScope, // 当前还是全局
            };
          });

        return all.concat({
          ...item,
          actions,
        });
      }
      return all;
    }, []);
    //
  }
}

export function isLengthArray(arr) {
  return !!(Array.isArray(arr) && arr.length);
}

export function editPageModelLInk(type: screenEditModel, history) {
  const { pathname, hash } = window.location;
  if (!timeOut) {
    let url = (pathname + hash).replace('/workspace', '');
    if (screenEditModel.CANVAS === type) {
      url = url.replace('/node/view', '/create');
    } else if (screenEditModel.NODES === type) {
      url = url.replace('/create', '/node/view');
    }
    timeOut = setTimeout(() => {
      history?.push(url);
      timeOut = null;
    }, 300);
  }
}

export function getBindId(id: string) {
  if (id) {
    const filterId = id.match(/(\(\S+\))/);
    if (filterId && isLengthArray(filterId)) {
      let id = filterId?.[0];
      if (id) {
        id = id.substring(1, id.length - 1);
        return id;
      }
    }
  }
  return id;
}

function defaultArguments() {
  const nodeCardWidth = 160,
    spaceBetween = 48;
  return {
    groupHeader: 30,
    spaceBetween, // 间距
    nodeCardWidth, //卡片宽度
    conditionWidth: 120, // 自定义条件宽度
    eventsHeight: 24, // 单条事件/动作高度
    otherHeight: 150, //卡片其他高度
    groupWidth: nodeCardWidth + spaceBetween * 2, //组最小宽度
  };
}

function getEvents(screen) {
  const { triggers = [], id } = screen;
  return [defaultTriggers, ...triggers].map((d) => {
    return {
      text: d.name,
      id: `${d.value}-${id}`,
    };
  });
}

async function forEach(array, callback) {
  if (isLengthArray(array)) {
    const maxLen = array.length;
    let current = 0;
    const sync = async () => {
      if (array[current]) {
        callback(array[current], current);
      }
      current++;
      if (current < maxLen) {
        await sync();
      }
    };
    await sync();
  }
}

function isFamiliarNode(type) {
  const { COMPONENT, CONDITION, PANEL_CARD, GROUP_CARD, REF, STATE_CARD } = LayersType;
  return [COMPONENT, CONDITION, REF, PANEL_CARD, GROUP_CARD, STATE_CARD].includes(type);
}

export function isGroupNode(type) {
  const { PANEL, STATE, GROUP } = LayersType;
  return [PANEL, STATE, GROUP].includes(type);
}

export function isGroupCardNode(type) {
  const { PANEL_CARD, GROUP_CARD, STATE_CARD } = LayersType;
  return [PANEL_CARD, GROUP_CARD, STATE_CARD].includes(type);
}

// 数据转换
export class treeDataConversion {
  maxHeight: number;
  col: number;
  row: number;
  data: any[];
  groups: any[];
  nodes: any[];
  nodeCardWidth: number;
  conditionWidth: number;
  componentsById: ByIdType;
  screensById: ByIdType;
  panelsById: ByIdType;
  maxBoxHeight: number;
  spaceBetween: number;
  groupHeader: number;
  lastLeft: number;
  lastTop: number;
  otherHeight: number;
  eventsHeight: number;
  constructor(data, componentsById, screensById, panelsById) {
    const { spaceBetween, nodeCardWidth, groupHeader, conditionWidth, eventsHeight, otherHeight } =
      defaultArguments();
    this.spaceBetween = spaceBetween; // 间距
    this.lastTop = 0; //上次高度
    this.lastLeft = 0; // 上次节点宽度
    this.maxBoxHeight = 0;
    this.nodeCardWidth = nodeCardWidth; //卡片宽度
    this.conditionWidth = conditionWidth; // 自定义条件宽度
    this.eventsHeight = eventsHeight; // 单条事件/动作高度
    this.otherHeight = otherHeight; //卡片其他高度
    this.groupHeader = groupHeader;
    this.data = data;
    this.groups = [];
    this.nodes = [];
    this.componentsById = componentsById;
    this.screensById = screensById;
    this.panelsById = panelsById;
  }

  async updateSize(nodes) {
    const {
      spaceBetween, // 间距
      groupHeader,
    } = defaultArguments();
    const resultObject = {};
    const changeData = async (childs, layers) => {
      let groupLeftX = spaceBetween,
        groupTopY = spaceBetween + groupHeader,
        maxBoxHeight = 0;
      const childArr = childs;
      await forEach(childArr, async (value, index) => {
        const isWrap = index && !(index % 3);
        if (isWrap) {
          // 换行
          groupTopY += maxBoxHeight + spaceBetween;
          groupLeftX = spaceBetween; // 换行left 重置
        }
        const transformTopLeft = (width, height) => {
          const left = groupLeftX;
          groupLeftX += width + spaceBetween; // 更新
          const top = groupTopY;
          const currentHeight = height;
          if (maxBoxHeight < currentHeight) {
            maxBoxHeight = currentHeight;
          }
          return {
            top,
            left,
          };
        };
        if (isWrap) {
          maxBoxHeight = 0;
        }
        if (isGroupNode(value.type)) {
          // 组
          const nodes = layers
            .filter((d) => value.id == d.group)
            .map((d) => resultObject[d.id] || d);
          const { width, height } = this.getBoxSize(nodes);
          const nodesLen = nodes.length + 1 > 4 ? 4 : nodes.length + 1;
          const _width = width + nodesLen * spaceBetween;

          const _height =
            height + this.groupHeader + (Math.floor(nodes.length / 3) + 2) * spaceBetween;
          const { top, left } = transformTopLeft(_width, _height);
          value = {
            ...value,
            left,
            top,
            width: _width,
            height: _height,
          };
        } else {
          const { top, left } = transformTopLeft(value.width, value.height);
          // 组件
          value = {
            ...value,
            left,
            top,
          };
        }
        resultObject[value.id] = value;
      });
    };
    await forEach(nodes, async (values) => {
      await forEach([...values].reverse(), async (layer) => {
        const childs = values
          .filter((d) => d.group == layer.id)
          .map((x) => {
            const v = resultObject[x.id] || x;
            return v;
          });
        await changeData(childs, values);
      });
    });
    const baseData = await this.updateBaseSize(nodes, resultObject);
    return baseData;
  }

  updateBaseSize(result, resultObject) {
    // eslint-disable-next-line no-async-promise-executor
    return new Promise<any>(async (resolve) => {
      const {
        spaceBetween, // 间距
      } = defaultArguments();
      let leftX = 0,
        topY = 0,
        maxBoxHeight = 0;
      await forEach(result, async (value) => {
        topY += maxBoxHeight + spaceBetween; //更新y
        maxBoxHeight = 0;
        leftX = 0;
        await forEach(value, (item) => {
          const layer = resultObject[item.id] || item;
          if (!layer.group) {
            const transformTopLeft = (width, height) => {
              const left = leftX + spaceBetween;
              leftX += width + spaceBetween; // 更新
              const top = topY + spaceBetween;
              const currentHeight = height;
              if (maxBoxHeight < currentHeight) {
                maxBoxHeight = currentHeight;
              }
              return {
                top,
                left,
              };
            };

            // 不属于任何组
            if (isGroupNode(layer.type)) {
              const nodes = value.filter((d) => layer.id == d.group).map((d) => resultObject[d.id]);
              const nodesLen = nodes.length + 1 > 4 ? 4 : nodes.length + 1;
              const { width, height } = this.getBoxSize(nodes);
              const _width = width + nodesLen * spaceBetween;
              const _height =
                height + this.groupHeader + (Math.floor(nodes.length / 3) + 2) * spaceBetween;

              const { top, left } = transformTopLeft(_width, _height);
              // 组 width height left top
              resultObject[item.id] = {
                ...layer,
                width: _width,
                height: _height,
                left,
                top,
              };
            } else {
              const { top, left } = transformTopLeft(item.width, item.height);
              // 组件 left top
              resultObject[item.id] = {
                ...layer,
                left,
                top,
              };
            }
          }
        });
      });
      resolve(resultObject);
    });
  }

  getEdges(data, nodesById) {
    // 连线
    const dataFlat = data.flat(2);
    return data.reduce((all, currs) => {
      const arr = currs
        .map((cur) => {
          if (cur.type === LayersType.COMPONENT) {
            // 组件
            const component = this.componentsById[cur.id] || {};
            let childrenEdges: any[] = [];
            if (isLengthArray(cur?.children)) {
              // 有子组件
              const sourceId = cur.id,
                targetId = 'group_' + cur.id;
              childrenEdges = [
                {
                  id: `${sourceId}-${targetId}`,
                  target: `${targetId}`,
                  targetNode: `${targetId}`,
                  source: `${sourceId}`,
                  sourceNode: `${sourceId}`,
                },
              ];
            }

            if (isLengthArray(component.events)) {
              const edges = component.events
                .filter((d) => d && d.id)
                .map((d) => {
                  const eventId = d.id;
                  const actions = d.actions || [];
                  const trigger = d?.trigger;
                  const conditions = d.conditions;

                  const actionsEdges = actions
                    ?.filter((v) => v && v.id)
                    .reduce((complete, current) => {
                      if (isLengthArray(conditions)) {
                        // 有条件
                        const sourceId = `${cur.id}`;
                        const targetId = `${cur.id}-${d.id}`;
                        const componentToConditionEdges = {
                          id: `${sourceId}-${targetId}`,
                          target: `action-condition-${targetId}`,
                          targetNode: `${targetId}`,
                          source: `${trigger}-${sourceId}`,
                          sourceNode: `${sourceId}`,
                          eventId: eventId,
                          actionId: current.actionId,
                        };

                        let conditionTocomponentEdges = [];
                        if (isLengthArray(current.component)) {
                          conditionTocomponentEdges = current.component
                            .filter((d) => d)
                            .map((component) => {
                              const sourceId = `${cur.id}-${d.id}`;
                              let targetId = this.transformTargetId({
                                targetId: `${getBindId(component)}`,
                                nodesById,
                              });

                              const targetCard =
                                !dataFlat.some((d) => d.id === targetId) &&
                                dataFlat.find((d) => d.parentId === targetId);
                              if (targetCard) {
                                targetId = targetCard.id;
                              }

                              return {
                                id: `${sourceId}-${targetId}`,
                                target: `${current.action}-${targetId}`,
                                targetNode: `${targetId}`,
                                source: `event-condition-${sourceId}`,
                                sourceNode: `${sourceId}`,
                                eventId: eventId,
                                actionId: current.actionId,
                              };
                            });
                        }
                        return complete.concat(
                          componentToConditionEdges,
                          conditionTocomponentEdges,
                        );
                      } else {
                        if (isLengthArray(current.component)) {
                          return complete.concat(
                            current.component
                              .filter((d) => d)
                              .map((bindComponent) => {
                                const sourceId = `${cur.id}`;
                                const targetId = this.transformTargetId({
                                  targetId: `${getBindId(bindComponent)}`,
                                  nodesById,
                                });
                                let bindId = getBindId(bindComponent);
                                if (bindId && !nodesById[`${bindId}`] && isGroup(bindId)) {
                                  bindId = `${LayersType.GROUP_CARD}-${bindId}`;
                                }
                                const node = nodesById[`${bindId}`];
                                const groupCardToGroup: any[] = [];
                                if (node && isGroupCardNode(node.type)) {
                                  // 组件 - 组card
                                  const targetId = node.id;
                                  return {
                                    id: `${sourceId}-${targetId}`,
                                    target: `${current.action}-${targetId}`,
                                    targetNode: `${targetId}`,
                                    source: `${trigger}-${sourceId}`,
                                    sourceNode: `${sourceId}`,
                                    eventId: eventId,
                                    actionId: current.actionId,
                                  };
                                }
                                if (node && isGroupNode(node.type)) {
                                  // 组card -> 组 edge
                                  const groupSourceId = targetId;
                                  const groupTargetId = `${getBindId(bindComponent)}`;
                                  groupCardToGroup.push({
                                    id: `${groupSourceId}-${groupTargetId}`,
                                    target: groupTargetId,
                                    targetGroup: `${groupTargetId}`,
                                    source: groupSourceId,
                                    sourceNode: `${groupSourceId}`,
                                    eventId: eventId,
                                    actionId: current.actionId,
                                  });
                                }

                                // 组件 - 组件
                                return [
                                  {
                                    id: `${sourceId}-${targetId}`,
                                    target: `${current.action}-${targetId}`,
                                    targetNode: `${targetId}`,
                                    source: `${trigger}-${sourceId}`,
                                    sourceNode: `${sourceId}`,
                                    eventId: eventId,
                                    actionId: current.actionId,
                                  },
                                  ...groupCardToGroup,
                                ].filter((d) => d);
                              }),
                          );
                        }
                      }
                    }, []);

                  return actionsEdges;
                })
                .flat(4)
                .filter((d) => d);
              return edges.concat(childrenEdges);
            }
            return childrenEdges;
          } else if (isGroupNode(cur.type)) {
            const { type, id } = cur;
            const groupType = getGroupType(type);
            const _id = groupType + '-' + id;
            if (nodesById[_id]) {
              const sourceId = _id,
                targetId = id;
              return {
                id: `${sourceId}-${targetId}`,
                target: `${targetId}`,
                targetNode: `${targetId}`,
                source: `${sourceId}`,
                sourceNode: `${sourceId}`,
              };
            }
          }
        })
        .flat(2)
        .filter((d) => d);
      return all.concat(arr);
    }, []);
  }

  transformTargetId({ targetId, nodesById }) {
    const node = nodesById[targetId];
    if (node && isGroupNode(node.type)) {
      const id = getGroupType(node.type) + '-' + node.id;
      const item = nodesById[id];
      if (item) {
        return id;
      }
    }
    return targetId;
  }

  deepFindComponent(dataFlat, item) {
    const childs = dataFlat.filter((x) => x.group === (item.parentId || item.id));
    if (!childs.some((d) => d.type === LayersType.COMPONENT)) {
      return childs.some((d) => this.deepFindComponent(dataFlat, d));
    }
    return true;
  }

  filterNullGroup(data) {
    // 过滤空组
    if (isLengthArray(data)) {
      const dataFlat = data.flat(2);
      return data.map((d) => {
        return d.filter((item) => {
          if (isGroupNode(item.type)) {
            return this.deepFindComponent(dataFlat, item);
          } else if (isGroupCardNode(item.type)) {
            // 有组件控制组，但是组本身是空的
            return isBeLinked(item.parentId, this.componentsById);
          }
          return item;
        });
      });
    }
    return [];
  }

  async getData() {
    let result = this.getNodeCard();
    result = this.filterNullGroup(result).filter((d) => isLengthArray(d));
    const data = await this.updateSize(result);
    const edges = this.getEdges(result, data);
    const resultFlat = result.flat(2).map((d) => {
      return data[d.id] || d;
    });
    const groups = resultFlat.filter((d) => isGroupNode(d.type));
    const nodes = resultFlat.filter(
      (d) => isFamiliarNode(d.type) && (d.group ? groups.some((g) => g.id === d.group) : true),
    );

    return {
      nodes,
      groups,
      edges,
    };
  }

  getBoxSize(nodes) {
    let width = 0,
      height = 0;
    if (isLengthArray(nodes)) {
      const nodesChunk = chunk(nodes, 3);
      width = this.getSize(nodesChunk, 'width');
      height = this.getSize(nodesChunk, 'height');
    }
    return {
      width,
      height,
    };
  }

  getSize(nodesChunk, code): number {
    const isHeight = code === 'height';
    const rowMaxSize = nodesChunk.reduce((all, current) => {
      if (isHeight) {
        // 高度
        return all.concat(max(current.map((d) => d[code])));
      }
      return all.concat(
        current.reduce((count, item: any) => {
          const size = item && item[code] ? +item[code] : 0;
          return item ? (count += size) : count;
        }, 0),
      );
    }, []);
    if (isHeight) {
      return rowMaxSize.reduce((count, curr) => {
        return (count += curr);
      }, 0);
    }
    return max(rowMaxSize) || 0;
  }

  getNodeCard() {
    // 将处理好的数据，转换成节点编辑器期望的数据模型
    if (isLengthArray(this.data)) {
      const result = this.data
        .map((item) => {
          return this.getTreeFlat([item], this.componentsById) || [];
        })
        .filter((d) => d.length);
      return result;
    }
    return [];
  }

  getTreeFlat(data, componentsById) {
    return data
      .reduce((all, item) => {
        const { layers } = item;
        const value = this.getNode({
          componentsById,
          data: item,
        });
        if (isLengthArray(layers)) {
          const result = this.getTreeFlat(layers, componentsById);
          return value ? all.concat([value, ...result]) : all.concat(result);
        }
        return value ? all.concat(value) : all;
      }, [])
      .flat(3);
  }

  getState(component) {
    const { screenId, id, parent } = component;
    const screen = this.screensById[screenId];
    const layer = deepFindOne(screen.layers, (arg) => arg.id === id);
    if (layer) {
      if (layer?.hideDefault) {
        return State.DEFAULT_HIDDEN; // 默认隐藏
      } else if (!layer?.show) {
        return State.HIDDEN; //隐藏
      }
    } else if (!parent) {
      return State.ERROR; //丢失
    }
    return State.NORM; //正常
  }

  getNode({ data, componentsById }) {
    const { id, color, name, layers, type, parentGroupId, ...rest } = data;
    const component = componentsById[id];
    const { actions: componentActions = [] } = component || {};

    const group = parentGroupId && parentGroupId?.toString();
    const actions = componentActions.concat(defaultActions).map((d) => {
      return {
        text: d.name,
        id: `${d.value}-${id}`,
      };
    });
    const defaultHeight = actions.length * this.eventsHeight + this.otherHeight;
    if (component) {
      // 组件
      const events = getEvents(component);
      const componentState = this.getState(component);
      const currentHeight = (events.length + actions.length) * this.eventsHeight + this.otherHeight;
      const value = {
        ...rest,
        id: id + '',
        color,
        top: 0,
        group,
        left: 0,
        name,
        height: currentHeight,
        width: this.nodeCardWidth,
        type,
        events,
        actions,
        state: componentState,
      };

      // 条件节点
      const conditionNodes = component.events
        .filter((d) => isLengthArray(d.conditions))
        .map((d, index) => {
          const type = LayersType.CONDITION;
          const _id = `${id}-${d.id}`;
          return {
            ...d,
            screenId: component.screenId,
            name: `${name}-${d.name}${index + 1}`,
            id: _id,
            left: 0,
            state: componentState,
            width: this.nodeCardWidth,
            height: 58 + d.conditions.length * 28,
            top: 0,
            actions: [
              {
                id: `action-condition-${_id}`,
              },
            ],
            events: [
              {
                id: `event-condition-${_id}`,
              },
            ],
            group,
            type,
          };
        })
        .flat(2);

      return isLengthArray(conditionNodes) ? [value].concat(conditionNodes) : value;
    } else if (isRefScreen(id)) {
      // 引用面板
      const panel = this.panelsById[getId(id)];
      if (!panel) {
        return;
      }
      return {
        left: 0,
        top: 0,
        ...rest,
        id: id + '',
        color,
        group,
        name,
        height: defaultHeight,
        width: this.nodeCardWidth,
        actions,
        state: this.getState({ ...panel, id }),
        type: LayersType.REF,
      };
    } else {
      // 组 等非组件
      const value = {
        ...rest,
        id: id + '',
        name,
        group,
        top: 0,
        left: 0,
        width: 0,
        height: 0,
        color,
        type,
      };

      // 组/动态面板 卡片
      const groupType = getGroupType(type);
      const _id = groupType + '-' + id;
      const card = {
        left: 0,
        top: 0,
        ...rest,
        id: _id,
        parentId: id + '',
        color,
        group,
        name,
        height: defaultHeight,
        width: this.nodeCardWidth,
        actions: defaultActions.map((d) => {
          return {
            text: d.name,
            id: `${d.value}-${_id}`,
          };
        }),
        state: State.NORM,
        type: groupType,
      };
      return [card, value];
    }
  }
}

function getGroupType(type) {
  if (type === LayersType.GROUP) {
    return LayersType.GROUP_CARD;
  } else if (type === LayersType.STATE) {
    return LayersType.STATE_CARD;
  } else if (type === LayersType.PANEL) {
    return LayersType.PANEL_CARD;
  }
  return LayersType.UNKNOWN;
}

// 十六进制转rgba
export function transferColorToRgba(color, alpha = 1) {
  if (typeof color !== 'string' && !(color instanceof String))
    return console.error('请输入16进制字符串形式的颜色值');
  color = color.charAt(0) === '#' ? color.substring(1) : color;
  if (color.length !== 6 && color.length !== 3) return console.error('请输入正确的颜色值');
  if (color.length === 3) {
    color = color.replace(/(\w)(\w)(\w)/, '$1$1$2$2$3$3');
  }
  const reg = /\w{2}/g;
  const colors = color.match(reg);
  for (let i = 0; i < colors.length; i++) {
    colors[i] = parseInt(colors[i], 16).toString();
  }
  return `rgba( ${colors.join()},${alpha} )`;
}

export function getPanelWithStates({ panelsById, screensById, id, name }) {
  if (!id) {
    return {};
  }

  const panelId = +id.replace(/panel_|ref_/g, '');
  const panel = panelsById[panelId];

  if (panel) {
    const { states = [] } = panel;
    return {
      title: name,
      value: id,
      color: Colors['动态面板'],
      children: states.reduce((result, stateId) => {
        const stateScreen = screensById[stateId];

        return stateScreen
          ? result.concat({
              title: stateScreen.name,
              value: stateId,
              color: Colors['动态面板'],
            })
          : result;
      }, []),
    };
  } else {
    return {
      title: name,
      color: Colors['动态面板'],
      value: id,
    };
  }
}

function getCategory(constants, moduleName) {
  const { category = '默认' } = constants.find((d) => d.moduleName === moduleName) || {};
  return Colors[category];
}

function isParticipateEvens(nodes, id) {
  // 是否参与事件
  const _id = typeof id === 'string' ? id : id + '';
  return nodes.some((d) => _id.includes(d));
}

export function getComponentOptions({
  screensById,
  panelsById,
  componentsById,
  screenId,
  constants,
  nodes,
}) {
  const screen = screensById[screenId];
  if (screen) {
    const { layers = [] } = screen;

    const deepMapLayers = (layers) => {
      return [...layers].reverse().map((item) => {
        if (isComponent(item.id)) {
          const componentWithChildren = getComponentLayerWithChildren({
            constants,
            componentsById,
            id: item.id + '',
            name: item.name,
            nodes,
          });
          return componentWithChildren;
        } else if (isPanel(item.id)) {
          const panelWithStates = getPanelWithStates({
            screensById,
            panelsById,
            id: item.id,
            name: item.name,
          });

          if (panelWithStates.children) {
            return {
              ...panelWithStates,
              cardId: getGroupType(LayersType.PANEL) + '-' + item.id,
              color: isParticipateEvens(nodes, item.id) && Colors['动态面板'],
              children: panelWithStates.children.map((d) => {
                const stateId = getId(d.value);
                return {
                  ...d,
                  cardId: getGroupType(LayersType.STATE) + '-' + stateId,
                  color: isParticipateEvens(nodes, stateId) && Colors['动态面板'],
                  children: getComponentOptions({
                    screensById,
                    componentsById,
                    panelsById,
                    screenId: stateId,
                    constants,
                    nodes,
                  }),
                };
              }),
            };
          } else {
            return {
              cardId: getGroupType(LayersType.PANEL) + '-' + item.id,
              ...panelWithStates,
              color: isParticipateEvens(nodes, item.id) && Colors['动态面板'],
            };
          }
        } else if (isRefScreen(item.id)) {
          return {
            title: item.name,
            value: item.id,
            color: isParticipateEvens(nodes, item.id) && Colors['引用面板'],
          };
        } else if (isGroup(item.id)) {
          return {
            title: item.name,
            value: item.id,
            cardId: getGroupType(LayersType.GROUP) + '-' + item.id,
            color: isParticipateEvens(nodes, item.id) && Colors['组'],
            children: deepMapLayers(item.components),
          };
        }
      });
    };

    return deepMapLayers(layers);
  } else {
    return [];
  }
}

function getComponentLayerWithChildren({ constants, componentsById, id, name, nodes }) {
  const component = componentsById[id] || {};
  let { children = [] } = component;
  const moduleName = component.base.module_name;
  if (children.length > 0) {
    children = children.reduce((result, childId) => {
      const child = componentsById[childId];
      const moduleName = child.base.module_name;
      return child
        ? result.concat({
            value: childId,
            color: isParticipateEvens(nodes, childId) && getCategory(constants, moduleName),
            title: child.name,
          })
        : result;
    }, []);

    return {
      title: name,
      value: id,
      color: isParticipateEvens(nodes, id) && getCategory(constants, moduleName),
      children: children,
    };
  } else {
    return {
      title: name,
      color: isParticipateEvens(nodes, id) && getCategory(constants, moduleName),
      value: id,
    };
  }
}

function findCurrentNode({ id, nodeData, validNodes, type = Type.UP }) {
  const { nodes, edges, groups } = nodeData;
  const cards = nodes.concat(groups);
  id = id + '';
  const isUp = type === Type.UP;
  const current = cards.find((d) => d.id.includes(id));
  if (current) {
    return edges
      .filter((d) => {
        return (isUp ? d.targetNode : d.sourceNode) === current.id;
      })
      .map((d) => (!isUp ? d.targetNode : d.sourceNode))
      .filter((id) => id && !validNodes.includes(id));
  }
  return [];
}

export function getAssociation({ id, nodeData, type }) {
  const callbackData = findNode({
    id,
    nodeData,
    type,
  });
  return callbackData;
}

export const findNode: FindNod = ({
  id,
  nodeData,
  callbackData = {
    nodes: [],
    edges: [],
  },
  type,
}) => {
  const nodes = findCurrentNode({
    id,
    nodeData,
    validNodes: callbackData.nodes,
    type,
  });
  if (type === Type.DOWN) {
    const downEdges = nodes
      .map((nodeId) => {
        const filterData = nodeData.edges.filter((edge) => edge.id === `${id}-${nodeId}`);
        return filterData.map((d) => d?.id);
      })
      .flat(2)
      .filter((d) => d);
    callbackData.edges = removeRepeat(callbackData.edges.concat(downEdges));
  } else if (type === Type.UP) {
    const upEdgeIds = nodes
      .map((nodeId) => {
        const filterData = nodeData.edges.filter((edge) => edge.id === `${nodeId}-${id}`);
        return filterData.map((d) => d?.id);
      })
      .flat(2)
      .filter((d) => d);
    callbackData.edges = removeRepeat(callbackData.edges.concat(upEdgeIds));
  }
  callbackData.nodes = Array.from(new Set(callbackData.nodes.concat(nodes)));
  if (nodes.length) {
    nodes.map((id) => {
      const current = findNode({ id, nodeData, callbackData, type });
      callbackData.nodes = removeRepeat(callbackData.nodes.concat(current.nodes));
      callbackData.edges = removeRepeat(callbackData.edges.concat(current.edges));
    });
  }
  return callbackData;
};

export function removeRepeat(data: Array<any>): Array<any> {
  return Array.isArray(data) ? Array.from(new Set(data)) : [];
}

export function isBeLinked(id: string | number, componentsById: ComponentsById) {
  // 看某组件是否存在事件关联关系 - （自身关联其他+其他关联自身）
  const currentComponent = componentsById[id];
  if (currentComponent && getActionsComponents(currentComponent)?.length) {
    return true;
  }

  // 其他关联自身
  return Object.values(componentsById).some((d) => {
    return getComponentBind(d)?.includes(id + '');
  });
}

// 获取组件身上绑定的组件/组/引用面板等
export function getComponentBind(component) {
  return Array.from(
    new Set(
      getActionsComponents(component)
        ?.map((event) => {
          return isLengthArray(event?.actions)
            ? event.actions?.map((d) => d?.component).flat(2)
            : [];
        })
        .flat(2),
    ),
  )
    .filter((d) => d)
    .map((id: string) => getBindId(id));
}

export function isEventNode(id) {
  return /[0-9]{1,}-event_/.test(id);
}

export function sourceNodeIdSplit(id: string) {
  if (id && isEventNode(id)) {
    return id.split('-event')[0];
  }
  return id;
}

// 获取上下游节点
export function getUpDownNode({ id, nodeData }): EventList {
  const currentNodeId = id;
  const { edges, nodes }: { nodes: Nodes[]; edges: Edges[] } = nodeData;
  const current = nodes.find((d) => d.id === id);
  function getStream(list: { id: string; text?: string }[] = [], targetCode, callbackCode) {
    if (Array.isArray(list) && targetCode) {
      return list
        .map((d) => {
          const result = edges.filter((e) => d.id?.includes(e?.[targetCode]));
          if (isLengthArray(result)) {
            const list = result
              .map((item) => {
                const node = nodes.find((n) => n.id === item?.[`${callbackCode}Node`]);
                if (node) {
                  const { id, name } = node;
                  const isSource = callbackCode == 'source';
                  const events = isSource ? node.events : node.actions;
                  const eventName = isEventNode(id)
                    ? { text: '条件执行' }
                    : events?.find((e) => e.id === item?.[callbackCode]);
                  const nodeId = isSource ? item.sourceNode : item.targetNode;
                  if (eventName?.text) {
                    return {
                      ...item,
                      name,
                      eventName: eventName?.text,
                      nodeId,
                      targetNodeId: !isSource
                        ? sourceNodeIdSplit(currentNodeId)
                        : sourceNodeIdSplit(item.sourceNode),
                      id: `${nodeId}-${d.id}`,
                    };
                  }
                }
              })
              .filter((d) => d);
            const ids = Array.from(new Set(list.map((d) => d?.nodeId)));
            const text = d.text || '条件执行';
            return {
              list: ids.map((nodeId) => list.find((d) => d?.nodeId === nodeId)).filter((d) => d),
              config: {
                ...d,
                text,
              },
            };
          }
        })
        .filter((d) => d);
    }
    return [];
  }
  let upStream, downStream;
  if (current) {
    // 当前
    const { events = [], actions = [] } = current;

    upStream = getStream(events, 'source', 'target'); // 下游
    downStream = getStream(actions, 'target', 'source'); // 上游
  }
  return {
    upStream,
    downStream,
  };
}
