import { isArray, isNullOrUnDef, isNumber, isString, isDate, isObject } from "@/utils/is";
import { FieldNamesProps } from "@/components/LjVxeTable/interface";
import XEUtils from "xe-utils";
import { cloneDeep, pick, get } from "lodash-es";
import dayjs from "dayjs";
import { useAuthButtons } from "@/hooks/useAuthButtons";
import { useUserStore } from "@/stores/modules/user";
import { GetFormulaCompute } from "@/api/modules/common";
import { ElMessage } from "element-plus";

import type { Menu } from "@/typings/global";

/**
 * @description 获取localStorage
 * @param {String} key Storage名称
 * @returns {String}
 */
export function localGet(key: string) {
  const value = window.localStorage.getItem(key);
  try {
    return JSON.parse(window.localStorage.getItem(key) as string);
  } catch (error) {
    return value;
  }
}

/**
 * @description 存储localStorage
 * @param {String} key Storage名称
 * @param {*} value Storage值
 * @returns {void}
 */
export function localSet(key: string, value: any) {
  window.localStorage.setItem(key, JSON.stringify(value));
}

/**
 * @description 清除localStorage
 * @param {String} key Storage名称
 * @returns {void}
 */
export function localRemove(key: string) {
  window.localStorage.removeItem(key);
}

/**
 * @description 清除所有localStorage
 * @returns {void}
 */
export function localClear() {
  window.localStorage.clear();
}

/**
 * @description 判断数据类型
 * @param {*} val 需要判断类型的数据
 * @returns {String}
 */
export function isType(val: any) {
  if (val === null) return "null";
  if (typeof val !== "object") return typeof val;
  else return Object.prototype.toString.call(val).slice(8, -1).toLocaleLowerCase();
}

/**
 * @description 生成唯一 uuid
 * @returns {String}
 */
export function generateUUID() {
  let uuid = "";
  for (let i = 0; i < 32; i++) {
    let random = (Math.random() * 16) | 0;
    if (i === 8 || i === 12 || i === 16 || i === 20) uuid += "-";
    uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16);
  }
  return uuid;
}

/**
 * 判断两个对象是否相同
 * @param {Object} a 要比较的对象一
 * @param {Object} b 要比较的对象二
 * @returns {Boolean} 相同返回 true，反之 false
 */
export function isObjectValueEqual(a: { [key: string]: any }, b: { [key: string]: any }) {
  if (!a || !b) return false;
  let aProps = Object.getOwnPropertyNames(a);
  let bProps = Object.getOwnPropertyNames(b);
  if (aProps.length != bProps.length) return false;
  for (let i = 0; i < aProps.length; i++) {
    let propName = aProps[i];
    let propA = a[propName];
    let propB = b[propName];
    if (!b.hasOwnProperty(propName)) return false;
    if (propA instanceof Object) {
      if (!isObjectValueEqual(propA, propB)) return false;
    } else if (propA !== propB) {
      return false;
    }
  }
  return true;
}

/**
 * @description 生成随机数
 * @param {Number} min 最小值
 * @param {Number} max 最大值
 * @returns {Number}
 */
export function randomNum(min: number, max: number): number {
  let num = Math.floor(Math.random() * (min - max) + max);
  return num;
}

/**
 * @description 获取当前时间对应的提示语
 * @returns {String}
 */
export function getTimeState(t: any) {
  // @ts-ignore
  let timeNow = new Date();
  let hours = timeNow.getHours();
  if (hours >= 6 && hours <= 10) return t("sys.greetings.morning") + ` ⛅`;
  if (hours >= 10 && hours <= 13) return t("sys.greetings.noon") + ` 🌞`;
  if (hours >= 13 && hours <= 18) return t("sys.greetings.afternoon") + ` 🌞`;
  if (hours >= 18 && hours <= 24) return t("sys.greetings.evening") + ` 🌛`;
  if (hours >= 0 && hours <= 6) return t("sys.greetings.deadOfNight") + ` 🌑`;
}

/**
 * @description 获取浏览器默认语言
 * @returns {String}
 */
export function getBrowserLang() {
  let browserLang = navigator.language ? navigator.language : navigator.browserLanguage;
  console.log("getBrowserLang browserLang :>> ", browserLang);
  let defaultBrowserLang = "";
  if (["cn", "zh", "zh-cn"].includes(browserLang.toLowerCase())) {
    defaultBrowserLang = "zh-cn";
  } else {
    defaultBrowserLang = "en";
  }
  return defaultBrowserLang;
}

/**
 * @description 使用递归扁平化菜单，方便添加动态路由
 * @param {Array} menuList 菜单列表
 * @returns {Array}
 */
export function getFlatMenuList(menuList: Menu.MenuOptions[]): Menu.MenuOptions[] {
  let newMenuList: Menu.MenuOptions[] = JSON.parse(JSON.stringify(menuList));
  // let newMenuList: Menu.MenuOptions[] = menuList;
  return newMenuList.flatMap(item => [item, ...(item.children ? getFlatMenuList(item.children) : [])]);
}

/**
 * @description 使用递归过滤出需要渲染在左侧菜单的列表 (需剔除 isHide == true 的菜单)
 * @param {Array} menuList 菜单列表
 * @returns {Array}
 * */
export function getShowMenuList(menuList: Menu.MenuOptions[]) {
  const { CheckPower } = useAuthButtons(undefined);
  let newMenuList: Menu.MenuOptions[] = cloneDeep(menuList);
  return newMenuList.filter(item => {
    if (item.meta.funid && !CheckPower(item.meta.funid)) {
      return false;
    }
    // 父级节点隐藏，跳过，不需要再递归子级节点
    !item.meta?.isHide && item.children?.length && (item.children = getShowMenuList(item.children));
    // 过滤隐藏节点
    return !item.meta?.isHide;
  });
}

/**
 * @description 使用递归找出所有面包屑存储到 pinia/vuex 中
 * @param {Array} menuList 菜单列表
 * @param {Array} parent 父级菜单
 * @param {Object} result 处理后的结果
 * @returns {Object}
 */
export const getAllBreadcrumbList = (menuList: Menu.MenuOptions[], parent = [], result: { [key: string]: any } = {}) => {
  for (const item of menuList) {
    result[item.path] = [...parent, item];
    if (item.children) getAllBreadcrumbList(item.children, result[item.path], result);
  }
  return result;
};

/**
 * @description 使用递归过滤出需要渲染在首页常用功能树 (需剔除 isHide == true 的菜单)
 * @param {Array} menuList 菜单列表
 * @returns {Array}
 * */
export function getQuickEnterFuncTree(menuList: Menu.MenuOptions[]): any[] {
  class TreeNode {
    key: string;
    label: string;
    children: TreeNode[] | null;
    icon?: string;
    path?: string;
    disabled: boolean;

    constructor(key: string, label: string, hasChild?: boolean) {
      this.key = key;
      this.label = label;
      this.children = hasChild ? [] : null;
    }

    public get getIcon(): string | undefined {
      return this.icon;
    }

    public set setIcon(v: string) {
      this.icon = v;
    }

    public get getPath(): string | undefined {
      return this.path;
    }

    public set setPath(v: string) {
      this.path = v;
    }
  }

  const { userInfo } = useUserStore();
  const recursiveFunTree = (children: Menu.MenuOptions[]): TreeNode[] => {
    let treeArray: TreeNode[] = [];
    for (const menu of children) {
      if (menu.meta?.isHide) continue;
      let treeNode = new TreeNode(menu.name, menu.meta?.title, isNullOrUnDef(menu.children));

      if (isNullOrUnDef(menu.component)) treeNode.path = menu.path;
      if (isNullOrUnDef(menu.meta?.icon)) treeNode.icon = menu.meta?.icon;
      if (!isNullOrUnDef(menu.meta?.funid))
        treeNode.disabled = userInfo?.rsltFunids && !userInfo?.rsltFunids.includes(menu.meta?.funid);
      if (isNullOrUnDef(menu.children)) continue;
      treeArray.push(treeNode);
      // 递归子集
      treeNode.children = recursiveFunTree(menu.children);
    }

    return treeArray;
  };

  console.log("getQuickEnterFuncTree menuList :>> ", menuList);
  let functionTree = recursiveFunTree(menuList);

  return functionTree;
}

/**
 * @description 使用递归过滤出需要渲染在首页常用功能树路由 (需剔除 isHide == true 的菜单)
 * @param {Array} menuList 菜单列表
 * @returns {Array}
 * */
export function getQuickEnterFuncTreeRouter(menuList: Menu.MenuOptions[]): Map<string, { [key: string]: any }> {
  console.log("getQuickEnterFuncTreeRouter menuList :>> ", menuList);
  class TreeNode {
    key: string;
    name: string;
    icon?: string;
    path: string;
    funid: number | string;

    constructor(key: string, label: string, path: string, icon?: string, funid?: number) {
      this.key = key;
      this.name = label;
      this.path = path;
      this.icon = icon || "";
      this.funid = funid ?? 0;
    }
  }

  const recursiveFunTree = (children: Menu.MenuOptions[]): Map<string, TreeNode> => {
    let routerMap: Map<string, TreeNode> = new Map();
    for (const menu of children) {
      // 跳过已隐藏的菜单 | 跳过不存在component的
      if (!(menu.meta?.isHide || isNullOrUnDef(menu.component))) {
        //
        let treeNode = new TreeNode(menu.name, menu.meta?.title, menu.path, menu.meta?.icon, menu.meta?.funid);
        routerMap.set(menu.name, treeNode);
      }

      if (isNullOrUnDef(menu.children)) continue;
      // 递归子集
      routerMap = new Map([...routerMap, ...recursiveFunTree(menu.children)]);
    }

    return routerMap;
  };

  let routerMap = recursiveFunTree(menuList);

  return routerMap;
}

/**
 * @description 使用递归处理路由菜单 path，生成一维数组 (第一版本地路由鉴权会用到，该函数暂未使用)
 * @param {Array} menuList 所有菜单列表
 * @param {Array} menuPathArr 菜单地址的一维数组 ['**','**']
 * @returns {Array}
 */
export function getMenuListPath(menuList: Menu.MenuOptions[], menuPathArr: string[] = []): string[] {
  for (const item of menuList) {
    if (typeof item === "object" && item.path) menuPathArr.push(item.path);
    if (item.children?.length) getMenuListPath(item.children, menuPathArr);
  }
  return menuPathArr;
}

/**
 * @description 递归查询当前 path 所对应的菜单对象 (该函数暂未使用)
 * @param {Array} menuList 菜单列表
 * @param {String} path 当前访问地址
 * @returns {Object | null}
 */
export function findMenuByPath(menuList: Menu.MenuOptions[], path: string): Menu.MenuOptions | null {
  for (const item of menuList) {
    if (item.path === path) return item;
    if (item.children) {
      const res = findMenuByPath(item.children, path);
      if (res) return res;
    }
  }
  return null;
}

/**
 * @description 使用递归过滤需要缓存的菜单 name (该函数暂未使用)
 * @param {Array} menuList 所有菜单列表
 * @param {Array} keepAliveNameArr 缓存的菜单 name ['**','**']
 * @returns {Array}
 * */
export function getKeepAliveRouterName(menuList: Menu.MenuOptions[], keepAliveNameArr: string[] = []) {
  menuList.forEach(item => {
    item.meta.isKeepAlive && item.name && keepAliveNameArr.push(item.name);
    item.children?.length && getKeepAliveRouterName(item.children, keepAliveNameArr);
  });
  return keepAliveNameArr;
}

/**
 * @description 格式化表格单元格默认值 (el-table-column)
 * @param {Number} row 行
 * @param {Number} col 列
 * @param {*} callValue 当前单元格值
 * @returns {String}
 * */
export function formatTableColumn(row: number, col: number, callValue: any) {
  // 如果当前值为数组，使用 / 拼接（根据需求自定义）
  if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "";
  return callValue ?? "";
}

/**
 * @description 处理值无数据情况
 * @param {*} callValue 需要处理的值
 * @returns {String}
 * */
export function formatValue(callValue: any) {
  // 如果当前值为数组，使用 / 拼接（根据需求自定义）
  if (isArray(callValue)) return callValue.length ? callValue.join(" / ") : "";
  return callValue ?? "";
}

/**
 * @description 处理 prop 为多级嵌套的情况，返回的数据 (列如: prop: user.name)
 * @param {Object} row 当前行数据
 * @param {String} prop 当前 prop
 * @returns {*}
 * */
export function handleRowAccordingToProp(row: { [key: string]: any }, prop: string, ...args: any) {
  if (args[0]) {
    return row[prop] && formatFn[args[0]] ? formatFn[args[0]]({ val: row[prop], format: args[0] }) : "";
  }
  if (!prop.includes(".")) return row[prop] ?? "";
  prop.split(".").forEach(item => (row = row[item] ?? ""));
  return row;
}

/**
 * @description 处理 prop，当 prop 为多级嵌套时 ==> 返回最后一级 prop
 * @param {String} prop 当前 prop
 * @returns {String}
 * */
export function handleProp(prop: string) {
  const propArr = prop.split(".");
  if (propArr.length == 1) return prop;
  return propArr[propArr.length - 1];
}

/**
 * @description 根据枚举列表查询当需要的数据（如果指定了 label 和 value 的 key值，会自动识别格式化）
 * @param {String} callValue 当前单元格值
 * @param {Array} enumData 字典列表
 * @param {Array} fieldNames label && value && children 的 key 值
 * @param {String} type 过滤类型（目前只有 tag）
 * @returns {String}
 * */
export function filterEnum(callValue: any, enumData?: any, fieldNames?: FieldNamesProps, type?: "tag") {
  const value = fieldNames?.value ?? "value";
  const label = fieldNames?.label ?? "label";
  const children = fieldNames?.children ?? "children";
  let filterData: { [key: string]: any } = {};
  // 判断 enumData 是否为数组
  if (Array.isArray(enumData)) filterData = findItemNested(enumData, callValue, value, children);
  // 判断是否输出的结果为 tag 类型
  if (type == "tag") {
    return filterData?.tagType ? filterData.tagType : "";
  } else {
    return filterData ? filterData[label] : "";
  }
}

/**
 * @description 递归查找 callValue 对应的 enum 值
 * */
export function findItemNested(enumData: any, callValue: any, value: string, children: string) {
  return enumData.reduce((accumulator: any, current: any) => {
    if (accumulator) return accumulator;
    if (current[value] === callValue) return current;
    if (current[children]) return findItemNested(current[children], callValue, value, children);
  }, null);
}

// 日期格式化
function formatDate({ val, format }: any) {
  return XEUtils.toDateString(val, format || "yyyy-MM-dd HH:mm:ss");
}
// 四舍五入金额，每隔3位逗号分隔，默认0位小数
export function formatIntNumber({ val }: any, digits = 0) {
  if (isNaN(Number(val))) return val;
  return XEUtils.commafy(XEUtils.toNumber(val), { digits });
}
// 四舍五入金额，每隔3位逗号分隔，默认2位数
export function formatAmount3({ val }: any, digits = 2) {
  if (isNaN(Number(val))) return val;
  return XEUtils.commafy(XEUtils.toNumber(val), { digits });
}
// 向下舍入,默认两位数
export function formatCutNumber({ val }: any, digits = 2) {
  if (isNaN(Number(val))) return val;
  if (Number(val) == 0) return 0;
  return XEUtils.toFixed(XEUtils.floor(val, digits), digits);
}
// 向下舍入,默认两位数
function formatCutNumber3({ val }: any, digits = 3) {
  if (isNaN(Number(val))) return val;
  return XEUtils.toFixed(XEUtils.floor(val, digits), digits);
}
// 四舍五入,默认两位数
export function formatFixedNumber({ val }: any, digits = 2) {
  if (isNaN(Number(val))) return val;
  return XEUtils.toFixed(XEUtils.round(val, digits), digits);
}
// 百分比：四舍五入金额，每隔3位逗号分隔，默认2位
function formatPercent({ val }: any, digits = 2) {
  if (isNaN(Number(val))) return val;
  return XEUtils.commafy(XEUtils.toNumber(val) * 100, { digits }) + "%";
}

/**
 * @description 格式化调用函数对照
 */
const formatFn: any = {
  "yyyy-MM-dd": formatDate,
  "yyyy-MM-dd HH:mm:ss": formatDate,
  "yyyy-MM-dd HH:mm": formatDate,
  "MM/dd/yyyy": formatDate,
  "MM/dd": formatDate,
  "dd/MM/yyyy": formatDate,
  "yyyy/MM/dd": formatDate,
  intNumber: formatIntNumber,
  cutNumber: formatCutNumber,
  amountNumber: formatAmount3,
  fixedNumber: formatFixedNumber,
  cutNumber3: formatCutNumber3,
  percent: formatPercent
};

/**
 * 一维,精简对象储存属性；支持数组型默认数据（关键字：field）
 * 逻辑：精简出与默认数据不同的属性，保留layoutAttr包含的字段
 * @param {Object} column 列数据
 * @param {String[]} layoutAttr 需要保留的数据名称
 * @param {Object | Array} layoutDefine 数据默认值 {...} Or [{field: xxx, ...}, ...]
 * @returns {Array} 返回精简后的对象
 */
export function streamlineFunc(column: any, layoutAttr: string[], layoutDefine: any) {
  let streamlinedColumns = [];

  if (Array.isArray(layoutDefine)) {
    for (let item of column) {
      const matchingDefinition = layoutDefine.find(itm => itm.field === item.field);

      const relevantAttrs = layoutAttr.filter(attr => {
        const itemValue = get(item, attr);
        const defineValue = get(matchingDefinition, attr);
        // 排除某些属性，并检查值是否不同
        return !["field", "table", "datatype"].includes(attr) && defineValue !== itemValue;
      });

      // 将 "field" 包含在过滤后的属性中
      const attrsToPick = [...relevantAttrs, "field"];
      streamlinedColumns.push(pick(item, attrsToPick));
    }
  } else {
    for (let item of column) {
      const relevantAttrs = layoutAttr.filter(attr => {
        // 检查值是否与布局定义不同
        return item[attr] !== layoutDefine[attr];
      });
      streamlinedColumns.push(pick(item, relevantAttrs));
    }
  }

  return streamlinedColumns;
}

/**
 * 一维,精简对象储存属性
 * @param {Object} column 列数据
 * @param {String[]} layoutAttr 需要保留的数据名称
 * @param {Object} layoutDefine 数据默认值
 * @param {String} attr 列数据的二级属性
 * @returns {Object} 返回精简后的对象
 */
export function streamlineAttrFunc(column: any, layoutAttr: string[], layoutDefine: any, attr: string) {
  let _map = new Map();
  for (let item of column) {
    let saveProp = cloneDeep(layoutAttr);
    if (item.basicinfo) {
      for (let key in layoutDefine) {
        if (item[attr].hasOwnProperty(key) && layoutDefine[key] === item[attr][key]) {
          saveProp.splice(saveProp.indexOf(key), 1);
        }
      }
      _map.set(item.field, pick(item[attr], saveProp));
    }
  }
  return _map;
}

/**
 * 提取目标数据差异属性
 * @param {Object} oriLayout 原始对象，带默认值
 * @param {Object} targetLayout 目标对象，需要提取差异的数据
 * @param {String[]} attrs 需要保留字符串名称，规则：仅需提供最底层属性名称
 * @param {String} arrayAttr 数组数据，关键字段，用于排序
 * @returns {Object}
 */
export function getDifference(oriLayout: any, targetLayout: any, attrs: string[] = [], arrayAttr = "id") {
  const diff: any = {};

  // 检查oriLayout中的属性
  for (const key in oriLayout) {
    if (oriLayout.hasOwnProperty(key)) {
      // 如果targetLayout中没有这个属性，则将它添加到差异中
      if (!targetLayout.hasOwnProperty(key)) {
        diff[key] = oriLayout[key];
      } else {
        // 如果targetLayout中也有这个属性，则比较它们的值
        if (Array.isArray(oriLayout[key])) {
          // 比较数组

          // 调整顺序，根据目标对象，调整原始对象数组顺序
          let _oriLayout = oriLayout[key];
          if (arrayAttr) {
            _oriLayout = oriLayout[key].slice().sort((a: any, b: any) => {
              let indexA = targetLayout[key].findIndex((item: any) => item[arrayAttr] === a[arrayAttr]);
              let indexB = targetLayout[key].findIndex((item: any) => item[arrayAttr] === b[arrayAttr]);
              return indexA - indexB;
            });
          }

          let arrAttr = [];
          for (const idx in _oriLayout) {
            console.log("oriLayout[key][idx] :>> ", _oriLayout[idx]);
            if (targetLayout[key][idx]) {
              const nestedDiff = getDifference(_oriLayout[idx], targetLayout[key][idx], attrs, arrayAttr);
              console.log(nestedDiff);
              if (Object.keys(nestedDiff).length > 0) {
                arrAttr.push(nestedDiff);
              }
            }
          }
          arrAttr.length && (diff[key] = arrAttr);
        } else if (typeof oriLayout[key] === "object" && typeof targetLayout[key] === "object") {
          // 递归比较对象
          console.log("key :>> ", key, oriLayout[key], targetLayout[key]);
          const nestedDiff = getDifference(oriLayout[key], targetLayout[key], attrs, arrayAttr);

          console.log("nestedDiff :>> ", key, nestedDiff);
          if (Object.keys(nestedDiff).length > 0) {
            console.log("nestedDiff :>> ", nestedDiff);
            diff[key] = nestedDiff;
          }
        } else if ((oriLayout[key] !== targetLayout[key] || key == arrayAttr) && attrs.includes(key)) {
          console.log("key :>> ", key);
          diff[key] = targetLayout[key];
        }
      }
    }
  }

  // 补充：检查targetLayout中的属性
  for (const key in targetLayout) {
    if (targetLayout.hasOwnProperty(key)) {
      // 如果oriLayout中没有这个属性，则将它添加到差异中
      if (
        !oriLayout.hasOwnProperty(key) ||
        (oriLayout.hasOwnProperty(key) && isObject(oriLayout[key]) && Object.keys(oriLayout[key]).length == 0)
      ) {
        diff[key] = targetLayout[key];
      }
    }
  }

  return diff;
}

/**
 * 读取数据到原始对象
 * @param {Object} oriLayout 原始对象，带默认值
 * @param {Object} targetLayout 差异的数据
 * @param {String} arrayAttr 数组数据，关键字段，用于排序
 * @returns {Object}
 */
export function setDifference(oriLayout: any, loadLayout: any, arrayAttr: string = "id") {
  let _oriLayout = cloneDeep(oriLayout);
  for (const key in loadLayout) {
    if (loadLayout.hasOwnProperty(key)) {
      if (Array.isArray(loadLayout[key])) {
        // console.log("setDifference _oriLayout :>> ", _oriLayout);
        let _sortedObj1 = _oriLayout[key].slice().sort((a: any, b: any) => {
          let indexA = loadLayout[key].findIndex((item: any) => item[arrayAttr] === a[arrayAttr]);
          let indexB = loadLayout[key].findIndex((item: any) => item[arrayAttr] === b[arrayAttr]);
          if (indexA == -1 || indexB == -1) {
            return 0;
          } else {
            return indexA - indexB;
          }
        });
        // console.log("setDifference _sortedObj1 :>> ", _sortedObj1);
        // console.log("setDifference loadLayout[key] :>> ", loadLayout[key]);

        let arr = [];
        for (const idx in _sortedObj1) {
          // console.log("idx :>> ", idx);
          // console.log("loadLayout[key][idx] :>> ", loadLayout[key][idx]);
          // console.log("_sortedObj1[idx] :>> ", _sortedObj1[idx]);
          // if (loadLayout[key][idx] && _sortedObj1[idx] && loadLayout[key][idx][arrayAttr] == _sortedObj1[idx][arrayAttr]) {
          //   arr.push(setDifference(_sortedObj1[idx], loadLayout[key][idx], arrayAttr));
          // }
          let itemIdx = loadLayout[key].findIndex((item: any) => item[arrayAttr] === _sortedObj1[idx][arrayAttr]);
          // console.log(itemIdx);
          let _loadLayout = itemIdx > -1 ? loadLayout[key][itemIdx] : {};
          arr.push(setDifference(_sortedObj1[idx], _loadLayout, arrayAttr));
        }
        // console.log("arr :>> ", arr);
        arr.length && (_oriLayout[key] = arr);
      } else if (typeof loadLayout[key] === "object") {
        if (!_oriLayout.hasOwnProperty(key)) {
          _oriLayout[key] = {};
        }
        _oriLayout[key] = setDifference(_oriLayout[key], loadLayout[key], arrayAttr);
      } else {
        _oriLayout[key] = loadLayout[key];
      }
    }
  }
  return _oriLayout;
}

/**
 * 金额缩略显示
 * @param s 数值
 * @param n 小数保留位数
 * @returns {string}
 */
export function numberFormat(s: number, n: number) {
  n = n !== undefined && n >= 0 && n <= 20 ? n : 2;
  let sum = Math.abs(Number(s));
  let sumStr = parseFloat((sum + "").replace(/[^\d\.-]/g, "")).toFixed(n) + "";
  let r = n ? "." + sumStr.split(".")[1] : "";
  let l = sumStr.split(".")[0].split("").reverse();
  let t = "";
  for (let i = 0; i < l.length; i++) {
    t += l[i] + ((i + 1) % 3 == 0 && i + 1 != l.length ? "," : "");
  }
  if (s < 0) return "-" + t.split("").reverse().join("") + r;
  return t.split("").reverse().join("") + r;
}

export const noop = () => {};

/**
 * 获取目标属性，转换字符串对象
 * @param obj 目标值
 * @param str 目标字符串属性
 */
export function convertStrToObj(obj: any, str?: string): any {
  console.log("convertStrToObj obj :>> ", obj);
  console.log("convertStrToObj str :>> ", str);
  if (!obj || JSON.stringify(obj) == "{}" || !str) return undefined;
  const keys = str.split(".");

  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    obj[key] = obj[key] || undefined;
    obj = obj[key];
  }

  return obj;
}

/**
 * 时间格式转义, detaTime to Diff
 * @param data {value:Object}
 * @returns {Object}
 */
export const getDiffToDate = (data: any) => {
  const today = dayjs(dayjs().format("YYYY-MM-DD"));
  for (const key in data) {
    if (key.indexOf("date") > -1) {
      if (isNumber(data[key])) {
        // number => string
        data[key] = today.add(data[key], "day").format("YYYY-MM-DD HH:mm:ss");
      } else if (isArray(data[key])) {
        // [number, number] => [string, string]
        data[key] = data[key].map((item: any) => {
          if (isNumber(item)) {
            return today.add(item, "day").format("YYYY-MM-DD HH:mm:ss");
          } else {
            return item;
          }
        });
      }
    }
  }
  console.log("getDiffToDate data :>> ", data);
  return data;
};

/**
 * 时间格式转义, Diff to DetaTime
 * @param data {value:Object}
 * @returns
 */
export const setDateToDiff = (data: any) => {
  const today = dayjs(dayjs().format("YYYY-MM-DD"));
  for (const key in data) {
    if (key.indexOf("date") > -1) {
      if (isString(data[key]) || isDate(data[key])) {
        // string => number
        let newDate = dayjs(dayjs(data[key]).format("YYYY-MM-DD"));
        data[key] = newDate.diff(today, "day");
      } else if (isArray(data[key])) {
        // [string, string] => [number, number]
        data[key] = data[key].map((item: any) => {
          let newDate = dayjs(dayjs(item).format("YYYY-MM-DD"));
          return newDate.diff(today, "day");
        });
      }
    }
  }
  console.log("_habit setDateToDiff data :>> ", data);
  return data;
};

/**
 * 捕捉报错提示
 * @param err 报错含有，关键字：会话、已与服务器失联、找不到网络路径
 * @returns {Boolean}
 */
export const ifErrorToLogin = (err: any) => {
  return err.indexOf("会话") >= 0 || err.indexOf("已与服务器失联") >= 0 || err.indexOf("找不到网络路径") >= 0;
};

/**
 * 遍历树形结构的数据
 * @param tree 遍历的树形结构数据
 * @param callback 回调函数，用于处理遍历到的每个节点
 */
export const traverseNode = (root: any, action: (node: any) => void) => {
  const queue = [root];

  while (queue.length > 0) {
    const node = queue.shift();
    action(node);
    if (node.children && node.children.length > 0) {
      for (const child of node.children) {
        queue.push(child);
      }
    }
  }
};

/*用正则表达式实现html转码*/
export const htmlEncodeByRegExp = (str: string) => {
  let s = "";
  if (str.length == 0) return "";
  s = str.replace(/&/g, "&amp;");
  s = s.replace(/</g, "&lt;");
  s = s.replace(/>/g, "&gt;");
  s = s.replace(/ /g, "&nbsp;");
  s = s.replace(/\'/g, "&#39;");
  s = s.replace(/\"/g, "&quot;");
  return s;
};
/*用正则表达式实现html解码*/
export const htmlDecodeByRegExp = (str: string) => {
  let s = "";
  if (str.length == 0) return "";
  s = str.replace(/&amp;/g, "&");
  s = s.replace(/&lt;/g, "<");
  s = s.replace(/&gt;/g, ">");
  s = s.replace(/&nbsp;/g, " ");
  s = s.replace(/&#39;/g, "'");
  s = s.replace(/&quot;/g, '"');
  return s;
};

/**
 * @description 加法
 * @param {Number} arg1 数1
 * @param {Number} arg2 数2
 * @returns
 */
export const floatAdd = (arg1: any, arg2: any) => {
  let r1, r2, m;
  try {
    r1 = arg1.toString().split(".")[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = arg2.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2));
  return (Math.round(arg1 * m) + Math.round(arg2 * m)) / m;
};
export const floatAddMore = (arr: any) => {
  let r1,
    r2,
    m,
    sum = 0;
  for (const item of arr) {
    if (item == undefined || item == null) continue;
    r1 = sum.toString().split(".")[1]?.length ?? 0;
    r2 = item.toString().split(".")[1]?.length ?? 0;
    m = Math.pow(10, Math.max(r1, r2));
    sum = (Math.round(sum * m) + Math.round(item * m)) / m;
  }

  return sum;
};
/**
 * @description 减法
 * @param arg1
 * @param arg2
 * @returns
 */
export const floatSub = (arg1: any, arg2: any) => {
  let r1, r2, m, n;
  try {
    r1 = arg1.toString().split(".")[1].length;
  } catch (e) {
    r1 = 0;
  }
  try {
    r2 = arg2.toString().split(".")[1].length;
  } catch (e) {
    r2 = 0;
  }
  m = Math.pow(10, Math.max(r1, r2));
  //动态控制精度长度
  n = r1 >= r2 ? r1 : r2;
  return ((Math.round(arg1 * m) - Math.round(arg2 * m)) / m).toFixed(n);
};
/**
 * @description 乘法
 * @param arg1
 * @param arg2
 * @returns
 */
export const floatMul = (arg1: any, arg2: any) => {
  let m = 0,
    s1 = (arg1 ?? 0).toString(),
    s2 = (arg2 ?? 0).toString();
  try {
    m += s1.split(".")[1].length;
  } catch (e) {}
  try {
    m += s2.split(".")[1].length;
  } catch (e) {}
  return (Number(s1.replace(".", "")) * Number(s2.replace(".", ""))) / Math.pow(10, m);
};
export const floatMulMore = (arr: any) => {
  let m = 0,
    sum = 1;
  for (const item of arr) {
    if (item) {
      let s1 = item.toString();
      try {
        m += s1.split(".")[1].length;
      } catch (e) {}
      sum = sum * Number(s1.replace(".", ""));
    }
  }
  return Number(sum) / Math.pow(10, m);
};
/**
 * @description 除法
 * @param arg1
 * @param arg2
 * @returns
 */
export const floatDiv = (arg1: any, arg2: any) => {
  let t1 = 0,
    t2 = 0,
    r1,
    r2;
  try {
    t1 = arg1.toString().split(".")[1].length;
  } catch (e) {}
  try {
    t2 = arg2.toString().split(".")[1].length;
  } catch (e) {}

  r1 = Number(arg1.toString().replace(".", ""));
  r2 = Number(arg2.toString().replace(".", ""));
  return (r1 / r2) * Math.pow(10, t2 - t1);
};

/**
 * @description 文件类型转化base64类型
 * @param type 文件类型
 * @returns base64类型
 */
export const getBase64Type = (type: string) => {
  switch (type.toLowerCase()) {
    case "txt":
      return "data:text/plain;base64,";
    case "doc":
      return "data:application/msword;base64,";
    case "docx":
      return "data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,";
    case "xls":
      return "data:application/vnd.ms-excel;base64,";
    case "xlsx":
      return "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,";
    case "pdf":
      return "data:application/pdf;base64,";
    case "pptx":
      return "data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,";
    case "ppt":
      return "data:application/vnd.ms-powerpoint;base64,";
    case "png":
      return "data:image/png;base64,";
    case "jpg":
      return "data:image/jpeg;base64,";
    case "gif":
      return "data:image/gif;base64,";
    case "svg":
      return "data:image/svg+xml;base64,";
    case "ico":
      return "data:image/x-icon;base64,";
    case "bmp":
      return "data:image/bmp;base64,";
    default:
      return "";
  }
};

/**
 * @description 自定义函数检查className中是否包含需要的类名
 */
export const classNameIncludes = (element: any, target: string[]) => {
  if (!element) return false;

  let _className: string[] = [];
  let parent = element.parentElement;

  // 获取当前元素的类名
  if (typeof element.className === "string") {
    _className = element.className.split(/\s+/g);
  }

  // 如果父元素存在，则获取父元素的类名
  while (parent) {
    if (parent.classList) {
      // 获取当前父元素的所有类名并添加到数组中
      Array.from(parent.classList).forEach((className: any) => {
        if (!_className.includes(className)) {
          _className.push(className);
        }
      });
    }
    // 移动到下一个父元素
    parent = parent.parentElement;
  }

  return target.some(item => _className.indexOf(item) > -1);
};

/**
 * @description 倒计时
 * @param {number} value 剩余秒数
 * @returns {string} 倒计时 00:00:30,
 */
export const countDownTime = (value: number) => {
  // 处理数字类型的秒数
  if (value <= 0) {
    return "";
  }

  const days = Math.floor(value / 86400);
  const hours = Math.floor((value % 86400) / 3600)
    .toString()
    .padStart(2, "0");
  const minutes = Math.floor((value % 3600) / 60)
    .toString()
    .padStart(2, "0");
  const seconds = (value % 60).toString().padStart(2, "0");

  let text = "";

  if (days > 365) {
    text = `${Math.floor(days / 365)}年`;
  } else if (days > 31) {
    text = `${Math.floor(days / 31)}月`;
  } else if (days > 0) {
    text = `${days}天`;
  } else {
    text = `${hours}:${minutes}:${seconds}`;
  }

  return text;
};

const parseTime = (time: any, cFormat?: string) => {
  let format = cFormat ?? `{y}-{m}-{d} {h}:{i}:{s}`;
  let date;
  if (typeof time === "object") {
    date = time;
  } else {
    if (typeof time === "string") {
      if (/^[0-9]+$/.test(time)) {
        time = parseInt(time);
      } else {
        time = time.replace(new RegExp(/-/gm), "/");
      }
    }

    if (typeof time === "number" && time.toString().length === 10) {
      time = time * 1000;
    }
    date = new Date(time);
  }
  const formatObj: any = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay()
  };
  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
    const value = formatObj[key];
    // Note: getDay() returns 0 on Sunday
    if (key === "a") {
      return ["日", "一", "二", "三", "四", "五", "六"][value];
    }
    return value.toString().padStart(2, "0");
  });
  return time_str;
};
/**
 * 最近时间-时差
 * @param {number} time
 * @param {string} option
 * @returns {Number} （秒）
 */
export const formatTime = function (time: string | number, option: string, ifshort: boolean, nowtime?: string) {
  const _time = new Date(time);
  const d = new Date(time);
  const now = nowtime ? new Date(nowtime) : new Date();

  const diff = (now.getTime() - d.getTime()) / 1000;

  if (ifshort) {
    if (diff < 0) {
      // flutter
    } else if (diff < 30) {
      return "刚刚";
    } else if (diff < 3600) {
      // less 1 hour
      return Math.ceil(diff / 60) + "分钟前";
    } else if (diff < 3600 * 6) {
      // 1~6小时
      return Math.ceil(diff / 3600) + "小时前";
    }
    let hourMins = parseTime(_time, "{h}:{i}");
    let isSameDay = now.setHours(0, 0, 0, 0) - d.setHours(0, 0, 0, 0);
    switch (isSameDay) {
      case 0:
        return `今天 ${hourMins}`;
      case 86400000:
        return `昨天 ${hourMins}`;
      // case 172800000:
      //     return `前天 ${hourMins}`
    }
    if (now.getFullYear() == d.getFullYear()) {
      return parseTime(_time, "{m}-{d} {h}:{i}");
    } else {
      return parseTime(_time, "{y}-{m}-{d} {h}:{i}");
    }
  }

  if (option) {
    return parseTime(_time, option);
  }
};

const funcGetFormulaValue = (item, tgList, fieldList, valueList) => {
  let _paras = item?.formula
    .replace(/【/g, "[")
    .replace(/】/g, "]")
    .match(/\[([^\]]*)\]/g);
  let resultFormula = item?.formula.replace(/【/g, "[").replace(/】/g, "]");

  _paras.forEach(part => {
    // console.log(part);
    let fieldName = part.slice(1, -1); // 去掉方括号
    // console.log(fieldName);
    let sumVal = tgList.find(f => {
      if (f.field === fieldName) {
        return f;
      } else if (f.label === fieldName) {
        fieldName = f.field;
        return f;
      }
    });
    !sumVal &&
      (sumVal = fieldList.find(f => {
        if (f.field === fieldName) {
          return f;
        } else if (f.label === fieldName) {
          fieldName = f.field;
          return f;
        }
      }));

    if (sumVal) {
      let res = valueList[fieldName];
      let _formula;
      if (sumVal?.formula) {
        _formula = sumVal.formula.replace(part, `${valueList[fieldName]}`);
      }
      // console.log(fieldName, _formula, sumVal);
      if (_formula && _formula.indexOf("[") > -1) {
        let _res = formulaPartsFormula([{ formula: _formula }], fieldList, valueList, tgList);
        res = `(${_res[0]})`;
      }
      if (typeof res === "undefined") {
        ElMessage.warning("接口未提供数据，请检查：" + fieldName);
        console.error("接口未提供数据，请检查：" + fieldName);
        return;
      }
      resultFormula = resultFormula.replace(part, res);
    } else {
      valueList[fieldName] && (resultFormula = resultFormula.replace(part, `${valueList[fieldName]}`));
    }
  });
  return resultFormula;
};

export const formulaPartsFormula = (formulaList, fieldList, valueList, tgList = formulaList) => {
  let result = [];
  formulaList.map((item, index) => {
    if (item?.formula) {
      let res = funcGetFormulaValue(item, tgList, fieldList, valueList);
      result.push(res);
    }
  });
  return result;
};

/**
 * @description 公式计算
 * @param formulaList 公式列表 [{field: 'a', formula: '[b] + [c]'}]
 * @param fieldList 字段列表 [{field: 'a', label: '字段A'}, {field: 'a', formula: '[b] + [c]'}]
 * @param valueList 值列表 {a: 1, b: 2, c: 3}
 * @returns {number[]} 计算结果 [1]
 */
export const calculateFormula = async (formulaList, fieldList, valueList) => {
  let resultFormula: any[] = formulaPartsFormula(formulaList, fieldList, valueList);

  console.log("resultFormula: ", resultFormula);
  try {
    let res = await GetFormulaCompute({
      formulas: resultFormula
    });
    if (res.values.length == 0) return [];
    let result = formulaList.map((item, index) => {
      let _res = res.values[index] ?? 0;
      return {
        ...item,
        value: _res
      };
    });
    return result;
  } catch (error) {
    console.error("Formula evaluation error:", error);
    return [];
  }
};

/**
 * @description LjVxeTable获取当前表格选中数据；默认第一条记录
 * @param targetRef 表格ref
 * @returns {$table, curRecords} 当前表格对象，当前选中数据
 */
export const getCurrentRecords = (targetRef: any) => {
  const $table = targetRef.element;

  const _records = $table.getCheckboxRecords() ?? [];
  const _cRecords = $table.getCurrentRecord() ?? null;

  let curRecords = [];
  if ($table) {
    if (_records.length) {
      // 获取勾选列表
      curRecords = _records;
    } else if (_cRecords) {
      // 获取当前选中数据
      curRecords = [_cRecords];
    } else {
      // 默认获取第一条记录
      let fullData = $table.getTableData().fullData;
      if (fullData.length) {
        curRecords = [fullData[0]];
        $table.setCurrentRow(fullData[0]);
      }
    }
  }

  return { $table, curRecords };
};

export const transformTreeData = (data: any) => {
  return data?.map(node => ({
    ...node.data,
    children: node.children ? transformTreeData(node.children) : []
  }));
};

/**
 * @description 过滤掉末尾的0
 * @param data
 * @returns
 */
export const isFilterPrice = (data, digits?: number) => {
  let _digits = digits ?? 2;
  let val = formatAmount3({ val: data }, _digits);
  // 过滤掉末尾的0
  let arr = val.split("");
  while (arr[arr.length - 1] === "0") {
    arr.pop();
  }
  return arr.join("");
};

/**
 * @description 计算vxetable的合并单元格
 * @param $table 表对象
 * @param fields 计算合并的字段明
 * @returns 需要合并的单元格数组
 */
export const autoMergeCells = ($table: any, fields: string[]) => {
  let result: any = [];
  let columns = $table.getColumns();
  let { visibleData: data } = $table.getTableData();
  // console.log("mergeCells data :>> ", columns, data);
  data.map((item: any, rowIndex: number) => {
    for (const key in item) {
      if (fields.includes(key)) {
        let rowspan = 1;
        let currentVal = "";
        let lastVal = "";
        let colIndex = 0;
        colIndex = columns.findIndex((item: any) => item.field == key);
        currentVal = item[key];
        if (rowIndex - 1 >= 0) {
          lastVal = data[rowIndex - 1][key];
        }
        // console.log("lastVal currentVal:>> ", colIndex, key, lastVal, currentVal);

        // if (rowIndex > 0) {
        // if (!lastVal) {
        if (lastVal != currentVal) {
          // 计算合并行数
          let _span = 0;
          for (let i = rowIndex + 1; i < data.length; i++) {
            if (data[i][key] && currentVal && data[i][key] == currentVal) {
              _span++;
            } else {
              break;
            }
          }
          rowspan += _span;
        } else {
          rowspan = 0;
        }
        // console.log("rowspan :>> ", key, lastVal, currentVal, rowspan);
        // }
        // }
        if (rowspan > 0) {
          result.push({
            row: rowIndex,
            col: colIndex,
            rowspan: rowspan,
            colspan: 1
          });
        }
      }
    }
  });
  return result;
};

export const text2Formula = (str: any) => {
  let rt = [];

  if (str.includes("以上") || str.includes("以下")) {
    // 匹配 "上下拼接X及以上Y以下"
    let rangeMatch = str.match(/(\d+)及以下/);
    if (rangeMatch) {
      const value = parseInt(rangeMatch[1], 10);
      rt.push(`value <= ${value}`);
    } else {
      rangeMatch = str.match(/(\d+)以下/);
      if (rangeMatch) {
        const value = parseInt(rangeMatch[1], 10);
        rt.push(`value < ${value}`);
      }
    }

    rangeMatch = str.match(/(\d+)及以上/);
    if (rangeMatch) {
      const value = parseInt(rangeMatch[1], 10);
      rt.push(`value >= ${value}`);
    } else {
      rangeMatch = str.match(/(\d+)以上/);
      if (rangeMatch) {
        const value = parseInt(rangeMatch[1], 10);
        rt.push(`value > ${value}`);
      }
    }
  } else {
    // 匹配 "上下拼接X"
    const exactMatch = str.match(/(\d+)/);
    if (exactMatch) {
      const value = parseInt(exactMatch[1], 10);
      rt.push(`value == ${value}`);
    }
  }

  if (rt.length > 0) {
    return rt.join(" && ");
  }

  throw new Error(`无法解析规则: ${str}`);
};

/**
 * @description 事件: 一键复制
 */
export const copyFunc = (data: any) => {
  const input = document.createElement("textarea");
  input.value = data;
  document.body.appendChild(input);
  input.select();
  document.execCommand("Copy");
  document.body.removeChild(input);
  ElMessage({
    type: "success",
    message: "复制成功"
  });
};
