'use strict';

const number = require('./number-4816603d.js');

/**
 * It flattens an array of objects
 *
 * @typedef T generic type  parameter
 * @param {T[]} arr - The array to be flattened.
 * @param {string} [children=children] - The name of the key that contains the children.
 * @returns {T[]}An array of all the children of the array of items.
 */
const flattenChildren = (arr, children = 'children') => {
  return arr.reduce((result, item) => result.concat(item[children] ? flattenChildren(item[children], children) : item), []);
};
/**
 * The function checks if two arrays are equal, either in order or ignoring order with specific comparator.
 *
 * @param {T[]} array1 - An array of type T, which is the first array to compare.
 * @param {T[]} array2 - `array2` is the second array that is being compared for equality with the
 * first array (`array1`).
 * @param {boolean} [ignoreOrder=false] - The `ignoreOrder` parameter is a boolean flag that determines
 * whether the order of elements in the arrays should be taken into account when comparing them. If
 * `ignoreOrder` is set to `true`, the function will return `true` if the two arrays contain the same
 * elements, regardless of their order
 * @param {boolean} [comparator=(a: T, b: T) => number] - The `comparator` parameter is the specific custom method used to compare the two arrays.
 * @returns {boolean} The function `isArrayEqual` returns a boolean value indicating whether the two input arrays
 * are equal or not. If the arrays are equal, it returns `true`, otherwise it returns `false`. The
 * equality of the arrays can be determined either by comparing the elements in the same order or by
 * ignoring the order of the elements.
 */
const isArrayEqual = (array1, array2, ignoreOrder = false, comparator = (a, b) => {
  if (a < b)
    return -1;
  if (a > b)
    return 1;
  return 0;
}) => {
  if (!Array.isArray(array1) || !Array.isArray(array2) || array1.length != array2.length)
    return false;
  if (ignoreOrder) {
    // Create copies to avoid mutating the original arrays
    const sortedArray1 = [...array1].sort(comparator);
    const sortedArray2 = [...array2].sort(comparator);
    return sortedArray1.every((element, index) => comparator(element, sortedArray2[index]) === 0);
  }
  else {
    return array1.every((element, index) => comparator(element, array2[index]) === 0);
  }
};
/**
 * Return true if all the keys of the object are numbers since it's an array-like object, false otherwise.
 *
 * @param {Record<string, unknown>} obj - The object to check.
 * @returns {boolean} boolean indicating if the object is array-like
 */
const isObjectArrayLike = (obj) => {
  return Object.keys(obj).every((key) => !isNaN(number.convertToNumber(key)));
};
/**
 * Return the longest text length In a string array
 *
 * @param {string[]} textArray string array
 * @returns {number} text length
 */
function getLongestTextLengthFromArray(textArray) {
  let length = 0;
  textArray.forEach((text) => {
    length = text && text.length > length ? text.length : length;
  });
  return length;
}
/**
 * Convert the single array to nested array base on the lengths of sub-arrays provided, will return the self array if length array not provided
 *
 * @example
 * // return [[1,2],[3,4,5,6]]
 * revertArrayFromFlatArray([1,2,3,4,5,6],[2,4])
 * @example
 * // return [1,2,3,4,5,6]
 * revertArrayFromFlatArray([1,2,3,4,5,6],[])
 * @param {T[]} flatArr - an array of elements that have been flattened into a single array.
 * @param {T[]} lengthArr - `lengthArr` is an array of numbers that specifies the length of each sub-array that the `flatArr`
 * @returns {T[] | T[][]} The function `revertArrayFromFlatArray` returns either a single-dimensional array of type `T[]` or a two-dimensional array of type `T[][]`
 */
function convertArrayToNestedArray(flatArr, lengthArr) {
  if (lengthArr.length) {
    const result = [];
    lengthArr.reduce((startIndex, length) => {
      result.push(flatArr.slice(startIndex, startIndex + length));
      return startIndex + length;
    }, 0);
    return result;
  }
  return flatArr;
}
/**
 * Convert nested array to simple array. the array only handle the directly children array
 *
 * @param {T[] | T[][]} arr the array data source
 * @returns {T[]} result simple array
 */
function flatNestedArray(arr) {
  if (arr && Array.isArray(arr[0])) {
    return arr.reduce((result, item) => {
      return result.concat(item);
    }, []);
  }
  return arr;
}
/**
 * sort number object array by given property, and the given property must be a number
 *
 * @param {T[] | T[][]} data data source
 * @param {(data: T) => number} sortBy the object item used to sort by
 * @returns {T[] | T[][]} result array
 */
function sortArrayOrNestedArray(data, sortBy) {
  let result = data;
  if (Array.isArray(data[0])) {
    result = data.map((item) => {
      item.sort((d1, d2) => sortBy(d1) - sortBy(d2));
      return item;
    });
  }
  else {
    result.sort((d1, d2) => sortBy(d1) - sortBy(d2));
  }
  return result;
}
/**
 * This function sorts an array or nested array based on a provided accessor function and an existing array of strings.
 *
 * @param {unknown[] | unknown[][]} needSortArray - The array or nested array that needs to be sorted.
 * @param {(item: T) => string} [accessor] - The accessor parameter is an optional function that takes an item from the
 * needSortArray and returns a string value that will be used for sorting. If not provided, the
 * function assumes that the items in the array are already strings.
 * @param {(item:string,nextItem:string)=>number} [existArraySorter] - the sort function for the exist array
 * @param {string[]} [existArray] - The `existArray` parameter is an optional array of strings that
 * represents the desired order of the elements in the `needSortArray`. If provided, the function will
 * sort the elements in `needSortArray` based on their position in `existArray`. If not provided, the default exist array will use the default order of the array
 * @returns {T[] | T[][]} The function `sortAsExistArray` returns a sorted array or nested array of type `T[] |
 * T[][]`. The sorting is based on the order of elements in the `existArray` parameter, or if
 * `existArray` is not provided, it is based on the order of appearance of elements in the input array
 * or nested array. The sorting is done using the `sortArrayOrNested
 */
function sortAsExistArray(needSortArray, accessor, existArraySorter, existArray) {
  // eslint-disable-next-line jsdoc/require-jsdoc
  const newAccessor = (item) => typeof accessor === 'function' ? accessor(item) : item;
  existArray = existArray
    ? existArray
    : Array.from(new Set(flatNestedArray(needSortArray).map(newAccessor)));
  const sorterArray = typeof existArraySorter === 'function' ? existArray.sort(existArraySorter) : existArray;
  const orderMap = sorterArray.reduce((result, text, index) => {
    result[text] = index;
    return result;
  }, {});
  // eslint-disable-next-line jsdoc/require-jsdoc
  const orderBy = (item) => orderMap[newAccessor(item)];
  return sortArrayOrNestedArray(needSortArray, orderBy);
}
/**
 * The function `findNodeList` iterates through a NodeList and returns the first node that satisfies a
 * given callback function.
 *
 * @param {NodeList} nodeList - NodeList is an array-like object that represents a collection of nodes.
 * It is typically returned by methods like `querySelectorAll` or `childNodes` in the DOM API. Each
 * element in the NodeList is a Node object.
 * @param {(node: Node) => boolean }callBack - The `callBack` parameter is a function that takes a `Node` as an argument and
 * returns a boolean value. It is used to filter and find a specific node within the `NodeList` based
 * on certain criteria defined in the callback function.
 * @returns {Node | undefined} The `findNodeList` function returns a `Node` object if the `callBack` function returns
 * `true` for any node in the `nodeList`. If none of the nodes satisfy the condition, it returns
 * `undefined`.
 */
function findNodeList(nodeList, callBack) {
  for (let index = 0; index < nodeList.length; index++) {
    const node = nodeList[index];
    if (callBack(node))
      return node;
  }
  return undefined;
}

exports.convertArrayToNestedArray = convertArrayToNestedArray;
exports.findNodeList = findNodeList;
exports.flatNestedArray = flatNestedArray;
exports.flattenChildren = flattenChildren;
exports.getLongestTextLengthFromArray = getLongestTextLengthFromArray;
exports.isArrayEqual = isArrayEqual;
exports.isObjectArrayLike = isObjectArrayLike;
exports.sortArrayOrNestedArray = sortArrayOrNestedArray;
exports.sortAsExistArray = sortAsExistArray;
