export const mapRecursive = (array, fn, childrenKey = 'children') => {
  return array.map((item) => {
    if (Array.isArray(item[childrenKey])) {
      return fn({
        ...item,
        [childrenKey]: mapRecursive(item[childrenKey], fn, childrenKey),
      });
    }
    return fn(item);
  });
};

// 展平有层级结构的 array
export const flatRecursive = (array, childrenKey = 'children') => {
  const result = [];
  mapRecursive(array, (item) => result.push(item), childrenKey);
  return result;
};

// 对有层级结构的 array 元素计数
export const countRecursive = (array, childrenKey = 'children') => {
  let counter = 0;
  mapRecursive(array, () => counter++, childrenKey);
  return counter;
};

// 对有层级结构的 array 进行 filter 处理
// 因为是 DFS 遍历, 可以借助 context.filteredChildren 在过滤/保留子结的同时保持父子结构 (即使父结点不满足筛选条件)
export const filterRecursive = (array, predicate, childrenKey = 'children') => {
  return array
    .map((item) => ({ ...item }))
    .filter((item) => {
      const children = item[childrenKey];
      if (Array.isArray(children)) {
        const filtered = filterRecursive(children, predicate, childrenKey);
        item[childrenKey] = filtered?.length ? filtered : undefined;
      }
      const context = { filteredChildren: item[childrenKey], isLeaf: !children?.length };
      return predicate(item, context);
    });
};

// "http://", "https://" 2 种情况
export const isUrl = (str) => {
  return /^https?:\/\//.test(str);
};

// 链接是否是 mailto 协议
export const isMailProtocol = (str) => {
  return /^mailto:/i.test(str.trim());
};

/**
 * @description 检测是否是 Iconify 格式的字符串
 * @deprecated
 */
export const isIconifyString = (str) => {
  return /^[\w-]+:[\w-]+$/.test(str);
};

/**
 * 检测 path 是否匹配当前 location, path 只考虑 "/" 开头的相对路径
 */
export const matchPath = (path) => {
  if (!path || !path?.startsWith('/')) {
    return false;
  }
  const ensureTrailingSlash = (str) => (str.endsWith('/') ? str : `${str}/`);
  const pathname = ensureTrailingSlash(window.location.pathname);
  const normalizedPath = ensureTrailingSlash(new URL(path, window.location.origin).pathname);
  return pathname.startsWith(normalizedPath);
};

/**
 * 从一组 paths 中, 找到匹配当前 location 的 path, 返回序号
 */
export const matchPaths = (paths = []) => {
  const matched = paths.map((item, index) => ({ path: item, index })).filter((item) => matchPath(item.path));
  if (!matched?.length) {
    return -1;
  }
  // 多个 path 都匹配时, 取一个最具体 (最长的) path
  const mostSpecific = matched.slice(1).reduce((prev, cur) => {
    return prev.path.length >= cur.path.length ? prev : cur;
  }, matched[0]);
  return mostSpecific.index;
};

/** 导航列表分列 */
export const splitNavColumns = (items, options = {}) => {
  const { columns = 1, breakInside = false, groupHeight = 48, itemHeight = 24, childrenKey = 'items' } = options;
  // 分组数
  const groups = items.length;

  // 高度预估
  const totalHeight = items.reduce((height, group) => {
    return height + groupHeight + (group[childrenKey]?.length || 0) * itemHeight;
  }, 0);
  const targetHeight = Math.ceil(totalHeight / columns);

  // 使用贪心策略进行分列
  const result = [[]];
  let currentColumn = 0;
  let currentHeight = 0;

  // 允许的高度偏差范围（有利于得到高度相差不大的列）
  const heightVariance = targetHeight * 0.2;

  // 是否应该分列
  const shouldBreakColumn = (nextHeight) => {
    return (
      currentHeight > targetHeight - heightVariance &&
      currentColumn < columns - 1 &&
      currentHeight + nextHeight > targetHeight + heightVariance
    );
  };

  items.forEach((group) => {
    // 当前分组的预估高度
    const groupTotalHeight = groupHeight + (group[childrenKey]?.length || 0) * itemHeight;

    // 允许截断分组，可以在任何子项处换列，尽可能利用当前列的剩余空间
    if (breakInside && shouldBreakColumn(groupHeight)) {
      currentColumn++;
      currentHeight = 0;
      result[currentColumn] = [];
    }
    // 不允许截断分组，只能在分组边界换列，优先分列，列不够用再考虑充分利用剩余空间
    if (!breakInside && currentHeight > 0 && (groups <= columns || shouldBreakColumn(groupTotalHeight))) {
      currentColumn++;
      currentHeight = 0;
      result[currentColumn] = [];
    }

    // 添加分组标题
    result[currentColumn].push({
      ...group,
      group: true,
    });
    currentHeight += groupHeight;

    // 添加子项
    if (group[childrenKey]) {
      group[childrenKey].forEach((child) => {
        if (breakInside && shouldBreakColumn(itemHeight)) {
          currentColumn++;
          currentHeight = 0;
          result[currentColumn] = [];
        }

        result[currentColumn].push({
          ...child,
          group: false,
        });
        currentHeight += itemHeight;
      });
    }
  });

  return result;
};
