import ansiConverter from "ansi-to-html";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
dayjs.extend(utc);
const GLASBEY = [
  [0, 155, 222],
  [87, 43, 255],
  [158, 73, 209],
  [184, 125, 112],
  [0, 128, 90],
  [253, 229, 0],
  [149, 75, 119],
  [140, 213, 104],
  [114, 58, 62],
  [63, 65, 172],
  [102, 162, 214],
  [206, 105, 193],
  [94, 89, 106],
  [237, 172, 136],
  [106, 166, 160],
  [230, 170, 210],
  [99, 0, 136],
  [219, 253, 0],
  [25, 40, 104],
  [255, 66, 180],
  [197, 89, 14],
  [67, 135, 23],
  [0, 211, 145],
  [255, 90, 249],
  [102, 116, 91],
  [179, 174, 142],
  [140, 125, 156],
  [198, 0, 70],
  [46, 78, 108],
  [70, 109, 166],
  [115, 137, 158],
  [202, 175, 168],
  [167, 141, 206],
  [100, 254, 0],
  [0, 121, 146],
  [161, 99, 255],
  [216, 255, 245],
  [241, 140, 1],
  [160, 172, 20],
  [90, 46, 91],
  [158, 134, 137],
  [187, 204, 208],
  [197, 175, 212],
  [109, 221, 219],
  [244, 255, 208],
  [134, 101, 0],
  [99, 105, 0],
  [104, 65, 168],
  [197, 151, 45],
  [255, 116, 169],
  [94, 187, 39],
  [0, 183, 88],
  [167, 255, 203],
  [171, 122, 164],
  [148, 189, 255],
  [193, 226, 137],
  [255, 201, 15],
  [197, 0, 213],
  [138, 109, 99],
  [143, 133, 105],
  [83, 78, 75],
  [104, 96, 171],
  [213, 182, 122],
  [23, 90, 44],
  [37, 0, 154],
  [243, 209, 190],
  [104, 111, 138],
  [107, 165, 106],
  [104, 84, 134],
  [186, 205, 174],
  [127, 153, 136],
  [0, 220, 203],
  [145, 4, 155],
  [27, 188, 235],
  [210, 156, 235],
  [111, 0, 112],
  [50, 161, 177],
  [147, 108, 202],
  [164, 70, 65],
  [138, 140, 229],
  [0, 69, 213],
  [203, 139, 199],
  [151, 150, 183],
  [118, 32, 212],
  [204, 75, 114],
  [0, 78, 104],
  [56, 34, 104],
  [79, 86, 56],
  [171, 187, 111],
  [49, 58, 134],
  [152, 211, 165],
  [143, 175, 185],
  [223, 228, 216],
  [224, 0, 171],
  [219, 193, 203],
  [140, 223, 255],
  [77, 83, 227],
  [111, 105, 102],
  [28, 0, 255],
  [115, 45, 83],
  [108, 145, 78],
  [17, 109, 168],
  [38, 159, 255],
  [176, 163, 95],
  [87, 133, 200],
  [152, 89, 146],
  [255, 161, 163],
  [186, 186, 254],
  [136, 42, 37],
  [168, 230, 219],
  [167, 242, 151],
  [214, 148, 103],
  [64, 91, 186],
  [146, 93, 58],
  [47, 79, 54],
  [150, 124, 39],
  [155, 149, 138],
  [87, 180, 208],
  [100, 71, 0],
  [47, 93, 95],
  [65, 142, 142],
  [19, 63, 173],
  [60, 150, 106],
  [133, 61, 161],
  [186, 183, 191],
  [103, 198, 172],
  [207, 105, 101],
  [0, 176, 146],
  [218, 227, 44],
  [54, 111, 1],
  [83, 121, 255],
  [127, 129, 66],
  [0, 233, 79],
  [40, 84, 153],
  [0, 10, 93],
  [88, 0, 163],
  [0, 136, 12],
  [167, 131, 90],
  [251, 236, 255],
  [1, 105, 75],
  [212, 118, 136],
  [255, 199, 230],
  [218, 255, 165],
  [120, 111, 216],
  [75, 2, 223],
  [92, 104, 106],
  [162, 107, 120],
  [103, 128, 126],
  [134, 71, 90],
  [202, 0, 0],
  [43, 0, 124],
  [114, 255, 151],
  [225, 227, 182],
  [201, 83, 220],
  [52, 120, 119],
  [142, 190, 88]
];
const isBigInt = (data) => typeof data === "bigint";
const isBoolean = (data) => typeof data === "boolean";
const isMap = (data) => data instanceof Map;
const isNullOrUndefined = (data) => data == null;
const isNumber = (data) => typeof data === "number";
const isObject = (data) => {
  return typeof data === "object" && !Array.isArray(data) && !isSet(data) && data !== null;
};
const isPrimitive = (data) => isBigInt(data) || isBoolean(data) || isNullOrUndefined(data) || isNumber(data) || isString(data) || isSymbol(data);
const isSet = (data) => data instanceof Set;
const isString = (data) => typeof data === "string";
const isSymbol = (data) => typeof data === "symbol";
const alphaNumericSorter = (a, b) => {
  if (a == null || b == null)
    return nullSorter(a, b);
  return a.toString().localeCompare(b.toString(), "en", { numeric: true });
};
const nullSorter = (a, b) => {
  if (a != null && b == null)
    return -1;
  if (a == null && b != null)
    return 1;
  return 0;
};
const toHtmlId = (str) => {
  return str.replace(/[\s_]/gi, "-").replace(/[^a-z0-9-]/gi, "").toLowerCase();
};
const truncate = (str, maxLength = 20, suffix = "...") => {
  if (maxLength < suffix.length + 1) {
    maxLength = suffix.length + 1;
  }
  if (str.length <= maxLength) {
    return str;
  }
  return str.slice(0, maxLength - suffix.length) + suffix;
};
const LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const CHARACTERS = `0123456789${LETTERS}`;
const DEFAULT_ALPHA_NUMERIC_LENGTH = 8;
const generateAlphaNumeric = (length = DEFAULT_ALPHA_NUMERIC_LENGTH, chars = CHARACTERS) => {
  let result = "";
  for (let i = length; i > 0; --i) {
    result += chars[Math.floor(Math.random() * chars.length)];
  }
  return result;
};
const generateUUID = () => {
  return [
    generateAlphaNumeric(8),
    generateAlphaNumeric(4),
    generateAlphaNumeric(4),
    generateAlphaNumeric(4),
    generateAlphaNumeric(12)
  ].join("-");
};
const clone = (data, deep = true) => {
  if (isPrimitive(data))
    return data;
  if (isMap(data))
    return new Map(data);
  if (isSet(data))
    return new Set(data);
  return deep ? JSON.parse(JSON.stringify(data)) : { ...data };
};
const DEFAULT_DATETIME_FORMAT = "YYYY-MM-DD, HH:mm:ss";
const formatDatetime = (datetime, options = {}) => {
  const config = {
    format: DEFAULT_DATETIME_FORMAT,
    inputUTC: false,
    outputUTC: true,
    ...options
  };
  const dateString = config.inputUTC ? stripTimezone(datetime) : datetime;
  let dayjsDate = dayjs.utc(dateString);
  if (!config.outputUTC)
    dayjsDate = dayjsDate.local();
  return dayjsDate.format(config.format);
};
const stripTimezone = (datetime) => {
  const timezoneRegex = /(Z|(-|\+)\d{2}:\d{2})$/;
  return datetime.replace(timezoneRegex, "");
};
const dateTimeStringSorter = (a, b) => {
  if (a == null || b == null)
    return nullSorter(a, b);
  const [aTime, bTime] = [new Date(a).getTime(), new Date(b).getTime()];
  if (aTime === bTime)
    return 0;
  return aTime < bTime ? -1 : 1;
};
const numericSorter = (a, b) => {
  if (a == null || b == null)
    return nullSorter(a, b);
  if (a === b)
    return 0;
  return a < b ? -1 : 1;
};
const converter = new ansiConverter({ newline: true });
const ansiToHtml = (ansi) => {
  const ansiWithHtml = ansi.replace(/(&|\u0026)/g, "&amp;").replace(/(>|\u003e)/g, "&gt;").replace(/(<|\u003c)/g, "&lt;").replace(/('|\u0027)/g, "&apos;").replace(/("|\u0022)/g, "&quot;");
  return converter.toHtml(ansiWithHtml);
};
const capitalize = (str) => {
  return str.split(/\s+/).map((part) => capitalizeWord(part)).join(" ");
};
const capitalizeWord = (str) => {
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
};
const findParentByClass = (element, className) => {
  const parent = element.closest(`.${className}`);
  if (parent)
    return parent;
  return element;
};
const glasbeyColor = (sequence) => {
  const index = sequence % GLASBEY.length;
  const rgb = GLASBEY[index];
  return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
};
const getTimeTickValues = (_self, rawValue) => {
  return rawValue.map((val) => dayjs.unix(val).format("hh:mm:ss.SSS").slice(0, -2));
};
const DEFAULT_PRECISION = 6;
const humanReadableNumber = (num, precision = DEFAULT_PRECISION) => {
  const stringNum = num.toString();
  let content = stringNum;
  if (isNaN(num)) {
    content = "NaN";
  } else if (!Number.isFinite(num)) {
    content = `${num < 0 ? "-" : ""}Infinity`;
  } else {
    const absoluteNum = Math.abs(num);
    if (absoluteNum !== 0 && absoluteNum < 0.01 || absoluteNum > 999) {
      content = num.toExponential(Math.max(precision, 0));
    } else if (!Number.isInteger(num)) {
      content = num.toFixed(Math.max(precision, 0));
    }
  }
  return content;
};
const findInsertionIndex = (sortedArray, value, compareFn = (a, b) => a - b) => {
  if (sortedArray.length === 0)
    return 0;
  if (compareFn(value, sortedArray[sortedArray.length - 1]) >= 0)
    return sortedArray.length;
  const getMidPoint = (start, end) => Math.floor((end - start) / 2) + start;
  let iEnd = sortedArray.length - 1;
  let iStart = 0;
  let iMiddle = getMidPoint(iStart, iEnd);
  while (iStart < iEnd) {
    const comparison = compareFn(value, sortedArray[iMiddle]);
    if (comparison === 0)
      return iMiddle;
    if (comparison < 0) {
      iEnd = iMiddle;
    } else {
      iStart = iMiddle + 1;
    }
    iMiddle = getMidPoint(iStart, iEnd);
  }
  return iMiddle;
};
function distance(x0, y0, x1, y1) {
  return Math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2);
}
const hexRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
const rgbaRegex = /^rgba?\(\s*?(\d+)\s*?,\s*?(\d+)\s*?,\s*?(\d+)\s*?(,\s*?([\d.]+)\s*?)?\)$/i;
const rgba2str = (rgba) => {
  if (rgba.a != null) {
    return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`;
  }
  return `rgb(${rgba.r}, ${rgba.g}, ${rgba.b})`;
};
const rgbaFromGradient = (rgba0, rgba1, percent) => {
  const r = Math.round((rgba1.r - rgba0.r) * percent + rgba0.r);
  const g = Math.round((rgba1.g - rgba0.g) * percent + rgba0.g);
  const b = Math.round((rgba1.b - rgba0.b) * percent + rgba0.b);
  if (rgba0.a != null && rgba1.a != null) {
    const a = (rgba1.a - rgba0.a) * percent + rgba0.a;
    return { a, b, g, r };
  }
  return { b, g, r };
};
const hex2rgb = (hex) => {
  const rgb = { b: 0, g: 0, r: 0 };
  const result = hexRegex.exec(hex);
  if (result && result.length > 3) {
    rgb.r = parseInt(result[1], 16);
    rgb.g = parseInt(result[2], 16);
    rgb.b = parseInt(result[3], 16);
  }
  return rgb;
};
const str2rgba = (str) => {
  if (hexRegex.test(str))
    return hex2rgb(str);
  const regex = rgbaRegex;
  const result = regex.exec(str);
  if (result && result.length > 3) {
    const rgba = { a: 1, b: 0, g: 0, r: 0 };
    rgba.r = parseInt(result[1]);
    rgba.g = parseInt(result[2]);
    rgba.b = parseInt(result[3]);
    if (result.length > 5 && result[5] != null)
      rgba.a = parseFloat(result[5]);
    return rgba;
  }
  return { a: 0, b: 0, g: 0, r: 0 };
};
const str2hsl = (str) => {
  return rgba2hsl(str2rgba(str));
};
const hex2hsl = (hex) => {
  return rgba2hsl(hex2rgb(hex));
};
const rgba2hsl = (rgba) => {
  const r = rgba.r / 255;
  const g = rgba.g / 255;
  const b = rgba.b / 255;
  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const avg = (max + min) / 2;
  const hsl = { h: Math.round(Math.random() * 6), l: 0.5, s: 0.5 };
  hsl.h = hsl.s = hsl.l = avg;
  if (max === min) {
    hsl.h = hsl.s = 0;
  } else {
    const d = max - min;
    hsl.s = hsl.l > 0.5 ? d / (2 - max - min) : d / (max + min);
    switch (max) {
      case r:
        hsl.h = (g - b) / d + (g < b ? 6 : 0);
        break;
      case g:
        hsl.h = (b - r) / d + 2;
        break;
      case b:
        hsl.h = (r - g) / d + 4;
        break;
    }
  }
  hsl.h = Math.round(360 * hsl.h / 6);
  hsl.s = Math.round(hsl.s * 100);
  hsl.l = Math.round(hsl.l * 100);
  return hsl;
};
const hsl2str = (hsl) => {
  return `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
};
const ensureArray = (data) => Array.isArray(data) ? data : [data];
export {
  alphaNumericSorter,
  ansiToHtml,
  capitalize,
  clone,
  dateTimeStringSorter,
  distance,
  ensureArray,
  findInsertionIndex,
  findParentByClass,
  formatDatetime,
  generateAlphaNumeric,
  generateUUID,
  getTimeTickValues,
  glasbeyColor,
  hex2hsl,
  hex2rgb,
  hsl2str,
  humanReadableNumber,
  isNumber,
  isObject,
  isString,
  numericSorter,
  rgba2hsl,
  rgba2str,
  rgbaFromGradient,
  str2hsl,
  str2rgba,
  toHtmlId,
  truncate
};
