import {
  nodeTypeList,
  NODE_TYPE,
  LEVEL_TYPE,
  nodeTypeRelations,
  poolNodePropPickMap,
  poolNodeExtraPropPickMap,
} from '@/utils/enum/node';
import type { TreeNode } from '@/utils/enum/node';
import { getMemberListIds } from './task';
import { CommonStore } from '@/stores/common';

const commonStore = CommonStore();
export const toMap = <T extends string | number, U>(
  list: { value: T; label: U }[]
): Record<T, U> => {
  return list.reduce(
    (obj, { value, label }) => {
      obj[value] = label;
      return obj;
    },
    {} as Record<T, U>
  );
};

export const flatNode = (arr: any) => {
  const data: any = [];
  const flat = (arr: any, parent?: any) => {
    arr?.forEach((item: any) => {
      item.parentId = parent?.fakeId || null;
      data.push(item);
      if (item.children?.length > 0) {
        flat(item.children, item);
      }
    });
  };
  flat(arr, null);
  return data;
};

export const getAllowedChildrenTypes = (parentType: string): string[] => {
  return nodeTypeRelations[parentType] || [];
};

export const canAddChild = (parent: TreeNode, childType: string): boolean => {
  if (!parent || !parent.nodeType) {
    return false;
  }
  if (parent.nodeType === NODE_TYPE.ROOT) {
    const childNodeType = isCategoryNodeType(childType) ? NODE_TYPE.CATEGORY : childType;
    return getAllowedChildrenTypes(parent.nodeType).includes(childNodeType);
  }
  const parentNodeType = isCategoryNodeType(parent.nodeType) ? NODE_TYPE.CATEGORY : parent.nodeType;
  const childNodeType = isCategoryNodeType(childType) ? NODE_TYPE.CATEGORY : childType;
  return getAllowedChildrenTypes(parentNodeType).includes(childNodeType);
};

export const isInnerNodeType = (nodeType: string): boolean => {
  const isInner = nodeTypeList.find((item) => item.value === nodeType);
  return !!isInner;
};
export const isCategoryType = (nodeType: string): boolean => {
  return nodeType === NODE_TYPE.CATEGORY;
};
export const isCategoryNodeType = (nodeType: string): boolean => {
  const categoryTypeList = commonStore.getDictData('POOL_CATEGORY_TYPE')?.A || [];
  return categoryTypeList.some((item: { value: string }) => item.value === nodeType);
};
export const isMixedNodeType = (nodeType: string): boolean => {
  return nodeType === NODE_TYPE.MIXED;
};

export const traverseTree = <T extends TreeNode>(
  nodes: T[] | undefined,
  callback: (node: T, parent: T | null) => void,
  parent: T | null
) => {
  if (!nodes) {
    return;
  }
  nodes.forEach((node) => {
    callback(node, parent);
    if (node.children && node.children.length > 0) {
      traverseTree(node.children as T[], callback, node);
    }
  });
};
export const traverseTreeAsync = async <T extends TreeNode>(
  tree: T[] | undefined,
  callback: (node: T, parent: T | null) => Promise<void> | void,
  parent: T | null = null
) => {
  if (!tree) {
    return;
  }
  const nodes = Array.isArray(tree) ? tree : [tree];
  for (const node of nodes) {
    await callback(node, parent);
    if (Array.isArray(node.children)) {
      await traverseTreeAsync(node.children as T[], callback, node);
    }
  }
};

export const generateFakeId = (id?: number | string): string => {
  const prefix = 'fakeId';
  const timePart = Date.now().toString(36);
  let randomPart = '';
  try {
    const cryptoObj = typeof globalThis !== 'undefined' ? (globalThis as any).crypto : undefined;
    if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
      const bytes = new Uint8Array(6);
      cryptoObj.getRandomValues(bytes);
      randomPart = Array.from(bytes)
        .map((b) => b.toString(16).padStart(2, '0'))
        .join('');
    } else {
      randomPart = Math.random().toString(36).slice(2, 8);
    }
  } catch {
    randomPart = Math.random().toString(36).slice(2, 8);
  }

  const idPart = id !== undefined && id !== null ? `_${String(id)}` : '';
  return `${prefix}_${timePart}_${randomPart}${idPart}`;
};

export const transformPoolNodesToTree = (nodes: TreeNode[]): TreeNode[] => {
  if (Array.isArray(nodes) && nodes.length > 0) {
    return nodes.map((node) => {
      const children = [];
      if (node.children) {
        children.push(...transformPoolNodesToTree(node.children));
      }
      if (Array.isArray(node.extras) && node.extras.length > 0) {
        node.extras.forEach((extra) => {
          node[extra.propertyName] = extra.propertyValue;
        });
      }
      if (node.nodeType === NODE_TYPE.TASK) {
      }
      return {
        ...node,
        fakeId: generateFakeId(node.id),
        children,
      };
    });
  }
  return [];
};

export const pickTreeNodeProp = (
  nodes: TreeNode[],
  pickMap: Record<string, string[]>,
  extraPickMap: Record<string, string[]>
): TreeNode[] => {
  return nodes.map((node) => {
    let keys = [];
    if (isCategoryNodeType(node.nodeType)) {
      keys = pickMap[NODE_TYPE.CATEGORY] || [];
    } else {
      keys = pickMap[node.nodeType] || [];
    }
    const newNode: TreeNode = {
      nodeType: node.nodeType,
    };
    keys.forEach((key) => {
      if (key in node) {
        newNode[key] = node[key];
      }
    });
    if (node.children && node.children.length > 0) {
      newNode.children = pickTreeNodeProp(node.children, pickMap, extraPickMap);
    } else {
      newNode.children = [];
    }
    const extras = getNodeExtras(node, extraPickMap);
    newNode.extras = extras.length > 0 ? extras : null;
    return newNode;
  });
};
export const getNodeExtras = (node: TreeNode, extraPickMap: Record<string, string[]>) => {
  let extras = [];
  if (!node.extras) {
    let props = [];
    if (isCategoryNodeType(node.nodeType)) {
      props = extraPickMap[NODE_TYPE.CATEGORY] || [];
    } else {
      props = extraPickMap[node.nodeType] || [];
    }
    extras = props.map((prop) => {
      return {
        propertyName: prop,
        propertyValue: node[prop] || '',
        valueType: '',
        propertyClass: '',
      };
    });
  } else if (Array.isArray(node.extras) && node.extras.length > 0) {
    extras = node.extras.map((item) => {
      return {
        ...item,
        propertyValue: node[item.propertyName],
      };
    });
  }
  return extras;
};
export const transformTreeToPoolNodes = (tree: TreeNode[]) => {
  traverseTree(
    tree,
    (node) => {
      if (![NODE_TYPE.TASK, NODE_TYPE.PERFORMANCE].includes(node.nodeType as NODE_TYPE)) {
        node.levelType = LEVEL_TYPE.NODE;
      } else {
        node.levelType = node.nodeType;
      }
      if (node.nodeType === NODE_TYPE.TASK) {
      }
      delete node._X_ROW_CHILD;
      delete node.type;
      delete node.fakeId;
      delete node.parentId;
    },
    null
  );
  return pickTreeNodeProp(tree, poolNodePropPickMap, poolNodeExtraPropPickMap);
};

// 递归处理项目loadcase数据
export const translateLoadcaseTree = (list: any[]) => {
  const arr = list.map((item) => {
    item.fakeId = item.uuid;
    item.pMemberIds = '';
    if (item.pMemberList?.length > 0) {
      item.pMemberIds = getMemberListIds(item.pMemberList);
    }
    item.eMemberIds = '';
    if (item.eMemberList?.length > 0) {
      item.eMemberIds = getMemberListIds(item.eMemberList);
    }
    if (Array.isArray(item.children)) {
      item.children = translateLoadcaseTree(item.children);
    }
    // el-tree的插入位置使用disable
    if (item.nodeType === NODE_TYPE.TASK || item.nodeType === NODE_TYPE.PERFORMANCE) {
      item.disabled = true;
    }
    return item;
  });
  return arr;
};
export const extractLeafNodesWithParentTypes = (list: any[] | undefined): any[] => {
  const result: any[] = [];
  const typeToField: Record<string, string> = {
    workspace: 'workspaceName',
    discipline: 'disciplineName',
    machine: 'machineName',
    task: 'taskName',
    performance: 'performanceName',
  };
  const typeToCodeField: Record<string, string> = {
    workspace: 'workspaceCode',
    task: 'taskCode',
  };

  const walk = (nodes: any[] | undefined, parents: any[]) => {
    if (!Array.isArray(nodes) || nodes.length === 0) return;
    for (const node of nodes) {
      const hasChildren = Array.isArray(node.children) && node.children.length > 0;
      if (!hasChildren) {
        const parentTypes = parents.map((p) => p && p.nodeType).filter(Boolean);
        const uniqueTypes = Array.from(new Set(parentTypes));
        const namesMap: Record<string, string[]> = {};
        parents.forEach((p) => {
          if (!p || !p.nodeType) return;
          const field = typeToField[p.nodeType];
          if (!field) return;
          namesMap[field] = namesMap[field] || [];
          const name = p.nodeName || p.nodeName === 0 ? String(p.nodeName) : undefined;
          if (name && !namesMap[field].includes(name)) {
            namesMap[field].push(name);
          }
        });
        const codesMap: Record<string, string[]> = {};
        parents.forEach((p) => {
          if (!p || !p.nodeType) return;
          const codeField = typeToCodeField[p.nodeType];
          if (!codeField) return;
          codesMap[codeField] = codesMap[codeField] || [];
          const code = p.nodeCode || p.nodeCode === 0 ? String(p.nodeCode) : undefined;
          if (code && !codesMap[codeField].includes(code)) {
            codesMap[codeField].push(code);
          }
        });
        const selfNameField = typeToField[node.nodeType];
        if (selfNameField) {
          namesMap[selfNameField] = namesMap[selfNameField] || [];
          const name = node.nodeName || node.nodeName === 0 ? String(node.nodeName) : undefined;
          if (name && !namesMap[selfNameField].includes(name)) {
            namesMap[selfNameField].push(name);
          }
        }
        const selfCodeField = typeToCodeField[node.nodeType];
        if (selfCodeField) {
          codesMap[selfCodeField] = codesMap[selfCodeField] || [];
          const code = node.nodeCode || node.nodeCode === 0 ? String(node.nodeCode) : undefined;
          if (code && !codesMap[selfCodeField].includes(code)) {
            codesMap[selfCodeField].push(code);
          }
        }
        const mergedNode = { ...node };
        if (node.nodeType === NODE_TYPE.PERFORMANCE) {
          const reversedParents = [...parents].reverse();
          const taskParent = reversedParents.find((p) => p && p.nodeType === NODE_TYPE.TASK);
          if (taskParent) {
            const excluded = new Set(['children', 'extras', 'fakeId', 'parentId']);
            for (const key in taskParent) {
              if (excluded.has(key)) continue;
              // 仅当 performance 本身没有该字段或为 undefined 时才拷贝，避免覆盖 performance 自有值
              if (!(key in mergedNode) || mergedNode[key] === undefined) {
                mergedNode[key] = taskParent[key];
              }
            }
          }
        }
        const extraProps: Record<string, string | null> = {};
        Object.values(typeToField).forEach((field) => {
          if (Array.isArray(namesMap[field]) && namesMap[field].length > 0) {
            extraProps[field] = namesMap[field].join(' , ');
          } else {
            extraProps[field] = null;
          }
        });
        Object.values(typeToCodeField).forEach((field) => {
          if (Array.isArray(codesMap[field]) && codesMap[field].length > 0) {
            extraProps[field] = codesMap[field].join(' , ');
          } else {
            extraProps[field] = null;
          }
        });
        const copy = {
          ...mergedNode,
          parentNodeTypes: uniqueTypes,
          ...extraProps,
          nodeType: NODE_TYPE.MIXED,
        };
        result.push(copy);
      } else {
        walk(node.children, [...parents, node]);
      }
    }
  };

  walk(list, []);
  return result;
};

export const findNodePath = (tree: TreeNode[], targetNode: TreeNode): TreeNode[] | null => {
  const dfs = (nodes: TreeNode[], path: TreeNode[]): TreeNode[] | null => {
    for (const node of nodes) {
      const currentPath = [...path, node];

      if (node.fakeId === targetNode.fakeId) {
        return currentPath;
      }

      if (node.children && node.children.length > 0) {
        const result = dfs(node.children, currentPath);
        if (result) return result;
      }
    }
    return null;
  };

  return dfs(tree, []);
};

export const extractCategoryNodeTypes = (nodePath: TreeNode[]): string[] => {
  return nodePath.filter((node) => isCategoryNodeType(node.nodeType)).map((node) => node.nodeType);
};

export const validateCategoryLevel = (
  selectedNodeType: string,
  parentCategoryPath: string[],
  tagOrder: string[]
): boolean => {
  if (!isCategoryNodeType(selectedNodeType)) {
    return true;
  }

  if (parentCategoryPath.length === 0) {
    return true;
  }

  const lastParentNodeType = parentCategoryPath[parentCategoryPath.length - 1];
  const selectedIndex = tagOrder.indexOf(selectedNodeType);
  const lastParentIndex = tagOrder.indexOf(lastParentNodeType);

  if (selectedIndex === -1 || lastParentIndex === -1) {
    return false;
  }

  return selectedIndex >= lastParentIndex;
};

export const collectNodeCodes = (nodes: TreeNode[], excludeFakeId?: string): string[] => {
  const codes: string[] = [];
  traverseTree(
    nodes,
    (node) => {
      if (node.nodeCode && node.fakeId !== excludeFakeId) {
        codes.push(node.nodeCode);
      }
    },
    null
  );
  return codes;
};
