/**
 * 判断对象类型
 * @param {*} obj 对象
 */
export function objTypeOf(obj: any): string {
  if (obj === null) return 'null';
  if (Array.isArray(obj)) return 'array';
  if (obj instanceof Date) return 'date';
  if (obj instanceof RegExp) return 'regExp';
  if (obj instanceof Blob) return 'blob';
  if (typeof obj === 'object') return 'object';
  return typeof obj;
}

/**
 * 将目标对象中有的属性值与源对象中的属性值合并
 * @param {Object} target 目标对象
 * @param {Object} sources 源对象
 */
export function objAssign<T extends Record<string, any>>(
  target: T,
  sources: Partial<T>
): T {
  const merged = {} as T;
  Object.keys(target).forEach((key) => {
    const propKey = key as keyof T;
    merged[propKey] = Object.prototype.hasOwnProperty.call(sources, propKey)
      ? (sources[propKey] as T[keyof T])
      : target[propKey];
  });

  return merged;
}
/**
 * 使用目标对象中有的属性值修改源对象中的属性值
 * @param {Object} target 目标对象
 * @param {Object} sources 源对象
 */
export function objModifyAssign<T extends Record<string, any>>(
  target: T,
  sources: Partial<T>
): void {
  Object.keys(target).forEach((k: keyof T) => {
    if (Object.prototype.hasOwnProperty.call(sources, k)) {
      target[k] = sources[k] as T[keyof T];
    }
  });
}

/**
 * 获取随机code,默认获取16位
 * @param {Number} len 推荐8的倍数
 *
 */
export function randomCode(len = 16) {
  if (len <= 0) return '';
  const steps = Math.ceil(len / 8);
  const stepNums = [];
  for (let i = 0; i < steps; i++) {
    const ranNum = Math.random().toString(32).slice(-8);
    stepNums.push(ranNum);
  }

  return stepNums.join('');
}

/**
 * 序列化参数
 * @param {Object} params 参数对象
 */
export function qsParams(params: Record<string, any>) {
  return Object.entries(params)
    .map((el) => `${el[0]}=${el[1]}`)
    .join('&');
}

/**
 *
 * @param {String} format 时间格式
 * @param {Date} date 需要格式化的时间对象
 */
export function formatDate(
  format = 'YYYY/MM/DD HH:mm:ss',
  date = new Date()
): string {
  if (objTypeOf(date) !== 'date') return '';
  const options = {
    'Y+': date.getFullYear(),
    'M+': date.getMonth() + 1,
    'D+': date.getDate(),
    'H+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds(),
  };
  Object.entries(options).forEach(([key, val]) => {
    if (new RegExp(`(${key})`).test(format)) {
      const zeros = key === 'Y+' ? '0000' : '00';
      const valLen = `${val}`.length;
      const value = (zeros + val).substr(valLen);
      format = format.replace(RegExp.$1, value);
    }
  });
  return format;
}

/**
 *  获取时间长度文字
 * @param {Number} timeNumber 时间数值，单位：毫秒
 */
export function timeNumberToText(timeNumber: number): string {
  const DAY_TIME = 24 * 60 * 60 * 1000;
  const HOUR_TIME = 60 * 60 * 1000;
  const MINUTE_TIME = 60 * 1000;
  const SECOND_TIME = 1000;
  let [day, hour, minute, second] = [0, 0, 0, 0];
  let residueTime = timeNumber;

  if (residueTime >= DAY_TIME) {
    day = Math.floor(residueTime / DAY_TIME);
    residueTime -= day * DAY_TIME;
  }
  if (residueTime >= HOUR_TIME) {
    hour = Math.floor(residueTime / HOUR_TIME);
    residueTime -= hour * HOUR_TIME;
  }
  if (residueTime >= MINUTE_TIME) {
    minute = Math.floor(residueTime / MINUTE_TIME);
    residueTime -= minute * MINUTE_TIME;
  }
  if (residueTime >= SECOND_TIME) {
    second = Math.round(residueTime / SECOND_TIME);
  }

  return [`${day}天`, `${hour}小时`, `${minute}分钟`, `${second}秒`]
    .filter((item) => !!item)
    .join('');
}

/**
 *  警告时间
 * @param {Number} timeNumber 时间数值，单位：毫秒
 * @param {Number} wainingTime 最大剩余警告时间数值，单位：毫秒
 */
export function residueFloorTime(timeNumber: number, wainingTime = 0) {
  if (timeNumber < 0) {
    return { status: 'danger', title: '已过期' };
  }

  const DAY_TIME = 24 * 60 * 60 * 1000;
  const HOUR_TIME = 60 * 60 * 1000;
  const MINUTE_TIME = 60 * 1000;
  const status = timeNumber < wainingTime ? 'warning' : 'primary';
  let [day, hour, minute] = [0, 0, 0];
  let residueTime = timeNumber;

  if (residueTime >= DAY_TIME) {
    day = Math.floor(residueTime / DAY_TIME);
    residueTime -= day * DAY_TIME;
    return {
      status,
      title: `剩余${day}天`,
    };
  }
  if (residueTime >= HOUR_TIME) {
    hour = Math.floor(residueTime / HOUR_TIME);
    residueTime -= hour * HOUR_TIME;
    return {
      status,
      title: `剩余${hour}小时`,
    };
  }
  if (residueTime >= MINUTE_TIME) {
    minute = Math.floor(residueTime / MINUTE_TIME);
    return {
      status,
      title: `剩余${minute}分钟`,
    };
  }

  return {
    status,
    title: `不足1分钟`,
  };
}

export function parseTimeRangeDateAndTime(startTime: number, endTime: number) {
  if (!startTime || !endTime)
    return {
      date: '',
      time: '',
    };

  const st = formatDate('YYYY-MM-DD HH:mm', new Date(startTime)).split(' ');
  const et = formatDate('YYYY-MM-DD HH:mm', new Date(endTime)).split(' ');

  return {
    date: st[0],
    time: `${st[1]}-${et[1]}`,
  };
}

export function parseTimeRangeDateAndTimeContent(
  startTime: number,
  endTime: number
) {
  const { date, time } = parseTimeRangeDateAndTime(startTime, endTime);
  return `${date} ${time}`;
}

/**
 * 获取本地时间，格式：年月日时分秒
 */
export function localNowDateTime() {
  return formatDate('YYYY年MM月DD日HH时mm分ss秒');
}

/**
 *
 * @param {Number} time 时间戳
 */
export function getTimeDatestamp(time: number) {
  const date = formatDate('YYYY-MM-DD HH:mm', new Date(time)).split(' ')[0];
  return new Date(`${date} 00:00:00`).getTime();
}

/**
 * 获取指定元素个数的数组
 * @param {Number} num
 */
export function getNumList(num: number) {
  return '#'.repeat(num).split('');
}

/**
 * 清除html标签
 * @param {String} str html字符串
 */
export function removeHtmlTag(str: string) {
  return str.replace(/<[^>]+>/g, '');
}

/**
 * 计算总数
 * @param {Array} dataList 需要统计的数组
 */
export function calcSum(dataList: number[]) {
  if (!dataList.length) return 0;
  return dataList.reduce((total, item) => {
    return total + item;
  }, 0);
}

/**
 * 计算评卷数
 * @param {Array} dataList 需要统计的数组
 */
export function calcAvg(dataList: number[]) {
  if (!dataList.length) return 0;
  return calcSum(dataList) / dataList.length;
}

/** 获取数组最大数 */
export function maxNum(dataList: number[]) {
  if (!dataList.length) return 0;
  return Math.max.apply(null, dataList);
}

export function isEmptyObject(obj: object) {
  return !Object.keys(obj).length;
}

export function humpToLowLine(a: string) {
  return a
    .replace(/([A-Z])/g, '-$1')
    .toLowerCase()
    .slice(1);
}

export function pickByNotNull<T extends Record<string, unknown>>(params: T) {
  const nData: Partial<T> = {};
  Object.entries(params).forEach(([key, val]) => {
    if (val !== null && val !== 'null' && val !== '')
      nData[key as keyof T] = val as T[keyof T];
  });
  return nData as T;
}

export function autoSubmitForm(url: string, params: Record<string, any>) {
  const form = document.createElement('form');
  form.action = url;
  form.method = 'post';

  Object.entries(params).forEach(([key, val]) => {
    const input = document.createElement('input');
    input.type = 'hidden';
    input.name = key;
    input.value = String(val);
    form.appendChild(input);
  });
  document.body.appendChild(form);
  form.submit();
}

export function blobToText(blob: Blob): Promise<string | ArrayBuffer | null> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsText(blob, 'utf-8');
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onerror = () => {
      reject();
    };
  });
}

export function parseHrefParam(
  urlStr: string,
  paramName = ''
): Record<string, string | string[]> | string | null {
  if (!urlStr) return null;
  const url = new URL(urlStr);

  const urlParams = new URLSearchParams(url.search);

  if (paramName) return urlParams.get(paramName as string);

  const params: Record<string, string | string[]> = {};
  urlParams.forEach((value, key) => {
    if (params[key]) {
      params[key] = [...params[key], value];
    } else {
      params[key] = value;
    }
  });
  return params;
}

/**
 * 将字典数据转成为option列表数据
 * @param data 字典数据
 * @returns option列表
 */
export function dictToOption(dict: Record<any, string>) {
  const options = [] as Array<{
    value: number | string | boolean;
    label: string;
  }>;
  const booleanStrs = ['true', 'false'];
  Object.keys(dict).forEach((k) => {
    options.push({
      value: booleanStrs.includes(k) ? (JSON.parse(k) as boolean) : k,
      label: dict[k],
    });
  });
  return options;
}

/**
 * 蛇形转驼峰
 * @param name 蛇形字符
 * @returns 驼峰字符
 */
export function snakeToHump(name: string): string {
  return name
    .split('-')
    .map((item) => item[0].toLocaleUpperCase() + item.substring(1))
    .join('');
}

export function deepCopy<T>(data: T): T {
  return JSON.parse(JSON.stringify(data));
}
