import type {
  dateListItemType,
  dayContentType,
  formatDateNum,
  formatDateStr,
  MessageTypes,
  dataType,
  deepLookupData
} from "../../types/tools";

import type {Action} from 'element-plus';
import {ElMessage, ElMessageBox } from "element-plus";
import type {MessageBoxState } from "element-plus";
import type {VNode} from "vue"
/*
  *获取某日期所在月份的日历天数
  *@param year 年份
  *@param month 月份
  * @param dayContent 日期内容
  *@param startDayOfMonday 是否以星期一开始，true为星期一开始，false为星期日开始
  *@returns 返回一个数组，包含当前月份的日历天数
  */
export const createCalendar = (
  year: number,
  month: number,
  dayContent: dayContentType[] = [],
  startDayOfMonday: boolean = true
): dateListItemType[] => {
  //当前月有多少天
  const daysInMonth:number = new Date(year, month + 1, 0).getDate();//下个月的第0天就是这个月的最后一天
  //上个月显示的天数
  const startDayOfWeek: 1|0 = startDayOfMonday ? 1 : 0;
  const monthStartOfWeek:number = new Date(year, month, 1).getDay();
  const daysInPrevMonth:number = new Date(year, month, 0).getDate();
  const prevMonthDays:number = monthStartOfWeek - startDayOfWeek;
  //下个月显示的天数
  const nextMonthDays:number = 42 - prevMonthDays - daysInMonth;

  // 创建一个数组，用于存放日历天数
  let calendar: dateListItemType[] = [];
  // 上一个月的最后几天
  for (let i = prevMonthDays; i > 0; i--) {
    calendar.push({
      ifCurrentMonth: false,
      day: daysInPrevMonth - i + 1,
      date: convertTimeFormat("YYYY-MM-DD", {
        year: year,
        month: month,
        day: daysInPrevMonth - i + 1
      })
    });
  }
  // 当前月份的天数
  for (let i = 1; i <= daysInMonth; i++) {
    calendar.push({
      ifCurrentMonth: true,
      day: i,
      date: convertTimeFormat("YYYY-MM-DD", {
        year: year,
        month: month + 1,
        day: i
      })
    });
  }
  // 下一个月的前几天
  for (let i = 1; i <= nextMonthDays; i++) {
    calendar.push({
      ifCurrentMonth: false,
      day: i,
      date: convertTimeFormat("YYYY-MM-DD", {
        year: year,
        month: month + 2,
        day: i
      })
    });
  }
  dayContent.forEach((item):void => {
    // @ts-ignore
    let index:number = calendar.findIndex((value) => value.date === item.date);
    if (index !== -1) {
      calendar[index].content = item.content;
    }
  })
  return calendar;
}
/**
 * 转换时间格式
 * @param format 时间格式
 * @param date 时间  formatDate对象 | Date对象 | 时间戳
 * @returns 返回一个字符串，格式为对应的时间格式
 * @example Tools.convertTimeFormat('YYYY-MM-DD HH:mm:ss',new Date())
 * */
export const convertTimeFormat = (format: string, date: formatDateNum | Date | number): string => {
  const padZero = (num: number | string): string => String(num).padStart(2, '0');

  const formatDate = (date: Date | number): formatDateStr => {
    const d = typeof date === 'number' ? new Date(date) : date;
    return {
      year: String(d.getFullYear()),
      month: padZero(d.getMonth() + 1),
      day: padZero(d.getDate()),
      hour: padZero(d.getHours()),
      minute: padZero(d.getMinutes()),
      second: padZero(d.getSeconds())
    };
  };

  const paramUnify = (date: formatDateNum | Date | number): formatDateStr => {
    if (typeof date === 'object' && ('year' in date || 'month' in date)) {
      return {
        year: String(date.year),
        month: padZero(date.month),
        day: padZero(date.day),
        hour: padZero(date.hour),
        minute: padZero(date.minute),
        second: padZero(date.second)
      };
    }
    return formatDate(date as Date | number);
  };

  const afterConvertDate = paramUnify(date);

  return format
    .replace('YYYY', afterConvertDate.year)
    .replace('MM', afterConvertDate.month)
    .replace('M', String(Number(afterConvertDate.month)))
    .replace('DD', afterConvertDate.day)
    .replace('D', String(Number(afterConvertDate.day)))
    .replace('HH', afterConvertDate.hour)
    .replace('H', String(Number(afterConvertDate.hour)))
    .replace('mm', afterConvertDate.minute)
    .replace('m', String(Number(afterConvertDate.minute)))
    .replace('ss', afterConvertDate.second)
    .replace('s', String(Number(afterConvertDate.second)));
};
/**
 * 根据模板直接转换日期格式
 * @param date 日期字符串年月日之间必须有任意非数字分隔符
 * @param targetFormat 目标的日期格式
 * @returns 返回一个字符串，格式为targetFormat的日期格式
 * @example Tools.convertDateFormatByTemplate('2021-01-01','YYYY年MM月DD日')
 * */
export const convertDateFormatByTemplate = (date: string, targetFormat:string): string =>  {
  const dateArr:RegExpMatchArray = date.match(/\d+/g) || ['error','error','error'];
  while(dateArr.length < 3){
    dateArr.push('error');
  }
  return targetFormat.replace('YYYY', dateArr[0]).replace('MM', dateArr[1]).replace('DD', dateArr[2]);
}

/**
 * 数组分类函数
 * @param array 需要分类的数组
 * @param rules 分类规则
 * @returns 返回一个对象，包含分类后的数组
 */
export const groupBy = (array:object[], rules: string | Function):object|{true:[],false:[]} =>{
  function paramUnify(rules:Function | string):Function{
    if(typeof rules === "string"){
      return eval(`(item)=>item.${rules}`);
    }
    return rules;
  }
  rules = paramUnify(rules);
  const result:object = {};
  for (const item of array) {
    const key = rules(item);
    if (!result[key]) {
      result[key] = [];
    }
    result[key].push(item);
  }
  return result;
}

/**
 * 深拷贝
 * @param data 需要拷贝的元素
 * @returns 深拷贝后的元素
 */
export const deepCopy = (data:any):any=>{
  let newData:any = null;
  if(typeof(data) === 'object' && data != null){
    newData = data instanceof Array ? [] : {};
    for( const key in data){
      newData[key] = deepCopy(data[key]);
    }
  }else{
    newData = data;
  }
  return newData;
}
/**
 * 根据key数组去重
 * @param array 需要去重的数组
 * @param key 去重的key
 * @returns  {*}
 */
export const accordingToKeyUnique = (array:Array<any>, key?:string):Array<any>=>{
  let newArray:Array<any> = [],obj:Object = {};
  if(key){
    newArray = array.reduce((preVal:any,curVal:any):any=>{
      obj[curVal[key]] ? "" : obj[curVal[key]] = preVal.push(curVal);
      return preVal;
    },[])
  }else{
    newArray = array.filter((val:any):Boolean=>{
      return obj.hasOwnProperty(typeof val + JSON.stringify(val)) ? false : obj[typeof val + JSON.stringify(val)] = true;
    })
  }
  return newArray;
}
/**
 * 防抖函数
 * @param fn 需要防抖的函数
 * @param delay 防抖时间
 */
export const debounce = (fn:Function, delay:number):Function=>{
  let timer = null;
  return function(...args){
    clearTimeout(timer);
    timer = setTimeout(()=>{
      fn.apply(this,args);
    },delay)
  }
}
/**
 * 节流函数
 * @param fn 需要节流的函数
 * @param delay 节流时间
 */
export const throttle = (fn:Function, delay:number):Function=>{
  let oldTime:number = Date.now();
  return function(...args){
    let newTime:number = Date.now();
    if(newTime - oldTime >= delay){
      fn.apply(this,args);
      oldTime = newTime;
    }
  }
}
/**
 * base64转file
 * @param urlData base64数据
 * @param fileName 文件名
 * @returns  返回一个file对象
 * */
export const base64ToFile = (urlData:string, fileName:string):File => {
  let arr:string[] = urlData.split(',');
  let mime:string = arr[0].match(/:(.*?);/)[1];
  let bytes:string = atob(arr[1]); // 解码base64
  let n:number = bytes.length
  let ia:Uint8Array = new Uint8Array(n);
  while (n--) {
    ia[n] = bytes.charCodeAt(n);
  }
  return new File([ia], fileName, { type: mime });
};
/**
 * file转base64
 * @param file file对象
 * @param callBack 回调函数
 * */
export const fileToBase64 = (file:File,callBack:(baseStr:any)=>void):void=> {
  const reader:FileReader = new FileReader();
  reader.onload = (e:ProgressEvent<FileReader>):void => {
    callBack(e.target.result)
  };
  reader.readAsDataURL(file);
}
/**
 * 获取数据类型
 * @param sourceData 源数据
 * @returns 返回数据类型
 */
export const getType = <T>(sourceData: T): dataType => {
  //toString会返回对应不同的标签的构造函数
  let toString = Object.prototype.toString;
  const map: any = {
    "[object Boolean]": "boolean",
    "[object Number]": "number",
    "[object String]": "string",
    "[object Function]": "function",
    "[object Array]": "array",
    "[object Date]": "date",
    "[object RegExp]": "regExp",
    "[object Undefined]": "undefined",
    "[object Null]": "null",
    "[object Object]": "object",
    "[object Blob]": "blob",
    "[object FormData]": "formData",
    "[object Promise]": "promise"
  }
  return map[toString.call(sourceData)];
}
/**
 * 显示消息框
 * @param type      消息类型（success / info / warning / error）
 * @param message   消息内容
 * @param offset    消息框距离窗口顶部的偏移量
 * @param duration  显示时间, 毫秒。设为 0 则不会自动关闭
 * @param grouping  合并内容相同的消息，不支持 VNode 类型的消息
 * @param dangerouslyUseHTMLString  是否将 message 属性作为 HTML 片段处理
 */
export const showMsg = (
  type: MessageTypes,
  message: string | VNode,
  offset: number = 60,
  duration: number = 3000,
  grouping: boolean = true,
  dangerouslyUseHTMLString?: boolean
):void => {
  ElMessage({
    type,
    message,
    offset,
    duration,
    grouping,
    dangerouslyUseHTMLString
  })
};
/**
 * 显示确认框
 * @param message                    消息内容
 * @param callback                   若不使用 Promise，可以使用此参数指定 MessageBox 关闭后的回调
 * @param title                      消息标题
 * @param type                       消息类型（success / info / warning / error）
 * @param cancel                     在外部自定义捕获异常时的回调
 * @param distinguishCancelAndClose  是否将取消（点击取消按钮）与关闭（点击关闭按钮或遮罩层、按下 Esc 键）进行区分
 * @param showCancelButton           是否显示取消按钮
 * @param showConfirmButton          是否显示确定按钮
 * @param confirmButtonText          确定按钮的文本内容
 * @param cancelButtonText           取消按钮的文本内容
 */
export const showConfirm = (
  message: string,
  callback: Function,
  title?: string,
  type?: MessageBoxState["type"],
  cancel?: Function,
  distinguishCancelAndClose: boolean = false,
  showCancelButton: boolean = true,
  showConfirmButton: boolean = true,
  confirmButtonText: string = "确定",
  cancelButtonText: string = "取消"
):void => {
  ElMessageBox.confirm(message, title ? title : "提示",
    {
      distinguishCancelAndClose: distinguishCancelAndClose,
      showCancelButton: showCancelButton,
      showConfirmButton: showConfirmButton,
      confirmButtonText: confirmButtonText,
      cancelButtonText: cancelButtonText,
      type: type as MessageBoxState["type"] ? type as MessageBoxState["type"] : "warning"
    },
  ).then(() => {
    callback();
  }).catch((action: Action) => {
    if (cancel) {
      if (action === "cancel") {
        cancel();
        return;
      }
    }
    showMsg("info", "取消操作");
  });
};
/**
 * 消息提示框
 * @param message                    消息内容
 * @param showClose                  MessageBox 是否显示右上角关闭按钮
 * @param title                      消息标题
 * @param type                       消息类型（success / info / warning / error）
 * @param distinguishCancelAndClose  是否将取消（点击取消按钮）与关闭（点击关闭按钮或遮罩层、按下 Esc 键）进行区分
 * @param showCancelButton           是否显示取消按钮
 * @param showConfirmButton          是否显示确定按钮
 * @param confirmButtonText          确定按钮的文本内容
 * @param cancelButtonText           取消按钮的文本内容
 * @param callback                   若不使用 Promise，可以使用此参数指定 MessageBox 关闭后的回调
 * @param cancel                     在外部自定义捕获异常时的回调
 */
export const showAlert = (
  message: string,
  showClose: boolean = true,
  title: string = "提示",
  type: MessageBoxState["type"] = "error",
  distinguishCancelAndClose: boolean = false,
  showCancelButton: boolean = false,
  showConfirmButton: boolean = false,
  confirmButtonText: string = "确定",
  cancelButtonText: string = "取消",
  callback: Function,
  cancel?: Function,
):void => {
  ElMessageBox.alert(message, title,
    {
      showClose: showClose,
      distinguishCancelAndClose: distinguishCancelAndClose,
      showCancelButton: showCancelButton,
      showConfirmButton: showConfirmButton,
      confirmButtonText: confirmButtonText,
      cancelButtonText: cancelButtonText,
      type: type
    },
  ).then(() => {
    callback();
  }).catch((action: Action) => {
    if (cancel) {
      if (action === "cancel") {
        cancel();
        return;
      }
    }
    showMsg("info", "取消操作");
  });
};

/**
 * 列表各项添加深度和位置信息
 * @param targetList 目标列表
 * @param indentStep 深度步进值
 * @param initialIndentValue 初始深度值
 * @param currentPos 第一项位置的初始值
 * @returns 处理后的列表和下一个位置
 */
export const calculateItemDepth = (
  targetList: any[],
  indentStep: number = 1,
  initialIndentValue: number = 0,
  currentPos: number = 0
): {
  nextPos: number;
  updatedList: { [p: string]: any; indentValue: number; children?: any[]; listPosition: number }[]
} => {
  let pos:number = currentPos;

  const updatedList = targetList.map(item => {
    let newItem = {
      ...item,
      indentValue: item.indentValue ? item.indentValue : initialIndentValue,
      listPosition: pos
    };
    pos += 1;

    if (newItem.children) {
      const result = calculateItemDepth(newItem.children, indentStep, initialIndentValue + indentStep, pos);
      // @ts-ignore
      newItem.children = result.updatedList;
      pos = result.nextPos;
    }

    return newItem;
  });

  return { updatedList, nextPos: pos };
};

/**
 * 深度搜索
 * @param dataList 目标数组
 * @param findRules 查找规则
 * @returns 返回一个数组，包含查找到的元素
 */
export const deepLookup = (dataList:Array<deepLookupData>,findRules:(item:deepLookupData)=>Boolean):Array<deepLookupData>=>{
  let result: Array<deepLookupData> = [];

  const search = (items: Array<deepLookupData>) => {
    for (const item of items) {
      if (findRules(item)) {
        result.push(item);
      }
      if (item.children && item.children.length > 0) {
        search(item.children);
      }
    }
  };

  search(dataList);
  return result;
}
/**
 * 复制文本
 * @param text 文本
 * @param ifShowMsg 是否显示消息
 * @returns 返回一个Promise对象 code=200成功，code=100失败
 */
export const copyText = (text:string,ifShowMsg:Boolean=true):Promise<{code:number,message:string}> => {

  return new Promise((resolve, reject)=>{
    if(navigator.clipboard){
      navigator.clipboard.writeText(text)
        .then(() => {
          if(ifShowMsg){showMsg('success','已复制!')}
          resolve({code:200,message:"已复制!"})
        })
        .catch(err => {
          if(ifShowMsg){showMsg('error','复制失败'+err)}
          resolve({code:100,message:"复制失败"+err})
        });
    }else{
      //Document.execCommand() 方法实现
      const codeElement = document.createElement('pre');
      codeElement.style.position = 'absolute';
      codeElement.style.left = '-9999px';
      codeElement.textContent = text;
      document.body.appendChild(codeElement);

      const range = document.createRange();
      range.selectNodeContents(codeElement);
      const selection = window.getSelection();
      selection.removeAllRanges();
      selection.addRange(range);

      try {
        const successful = document.execCommand('copy');
        if (successful) {
          if(ifShowMsg){showMsg('success','已复制!')}
          resolve({code:200,message:"已复制!"})
        } else {
          if(ifShowMsg){showMsg('error','复制失败')}
          resolve({code:100,message:"复制失败"})
        }
      } catch (err) {
        if(ifShowMsg){showMsg('error','复制失败'+err)}
        resolve({code:100,message:"复制失败"+err})
      } finally{
        document.body.removeChild(codeElement);
        selection.removeAllRanges();
      }
    }
  })
}
/**
 * 改变颜色
 * @param colorValue 16进制颜色值 或 rgb() 或 rgba()
 * @param degree 改变的程度,负数加深颜色/透明度变小；正数颜色变浅/透明度变大
 * @param originally 返回结果和原本类型一致,false时输出16进制颜色值
 * @returns 返回改变后的颜色值
 */
export const changeColor = (colorValue: string, degree: number, originally: boolean = true): string => {
  // 将十六进制颜色值转为 RGB 组件
  const hexToRgb = (hex: string) => {
    let r = 0, g = 0, b = 0, a = 1;
    if (hex.length === 4) {
      r = parseInt(hex[1] + hex[1], 16);
      g = parseInt(hex[2] + hex[2], 16);
      b = parseInt(hex[3] + hex[3], 16);
    } else if (hex.length === 7) {
      r = parseInt(hex[1] + hex[2], 16);
      g = parseInt(hex[3] + hex[4], 16);
      b = parseInt(hex[5] + hex[6], 16);
    } else if (hex.length === 9) { // #RRGGBBAA 格式
      r = parseInt(hex[1] + hex[2], 16);
      g = parseInt(hex[3] + hex[4], 16);
      b = parseInt(hex[5] + hex[6], 16);
      a = parseInt(hex[7] + hex[8], 16) / 255;
    }
    return { r, g, b, a };
  };

  // 将 RGB 组件转为十六进制颜色值
  const rgbToHex = (r: number, g: number, b: number) => {
    const toHex = (n: number) => {
      const hex = n.toString(16);
      return hex.length === 1 ? '0' + hex : hex;
    };
    return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
  };

  // 将 RGBA 转为带透明度的十六进制颜色值
  const rgbaToHex = (r: number, g: number, b: number, a: number) => {
    const toHex = (n: number) => {
      const hex = Math.round(n * 255).toString(16);
      return hex.length === 1 ? '0' + hex : hex;
    };
    return `${rgbToHex(r, g, b)}${toHex(a)}`;
  };

  // 解析颜色值
  let r, g, b, a = 1;
  let isHex = false;
  let hasAlpha = false;

  if (colorValue.startsWith('#')) {
    isHex = true;
    ({ r, g, b, a } = hexToRgb(colorValue));
    hasAlpha = colorValue.length === 9;
  } else if (colorValue.startsWith('rgb')) {
    const regex = /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/;
    const match = regex.exec(colorValue);
    if (match) {
      r = parseInt(match[1]);
      g = parseInt(match[2]);
      b = parseInt(match[3]);
      if (match[4]) {
        a = parseFloat(match[4]);
        hasAlpha = true;
      }
    }
  } else {
    throw new Error('Invalid color value');
  }

  // 改变颜色组件的值
  const changeColorComponent = (component: number, degree: number) => {
    return degree > 0
      ? Math.min(255, component + degree)
      : Math.max(0, component + degree);
  };

  // 改变透明度值
  const changeAlpha = (a: number, degree: number) => {
    return degree > 0
      ? Math.min(1, a + degree / 100)
      : Math.max(0, a + degree / 100);
  };

  // 应用颜色和透明度变化
  if (hasAlpha) {
    a = changeAlpha(a, degree);
  } else {
    r = changeColorComponent(r, degree);
    g = changeColorComponent(g, degree);
    b = changeColorComponent(b, degree);
  }

  if (originally) {
    // 如果 originally 为 true,返回与输入类型一致的颜色值
    if (isHex) {
      return hasAlpha ? rgbaToHex(r, g, b, a) : rgbToHex(r, g, b);
    } else {
      return hasAlpha ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
    }
  } else {
    // 如果 originally 为 false,返回十六进制颜色值,带有透明度
    return hasAlpha ? rgbaToHex(r, g, b, a) : rgbToHex(r, g, b);
  }
};
/**
 * 区间随机整数
 * @param min 最小值
 * @param max 最大值
 * @returns 返回一个随机整数
 */
export const randomInterval = (min,max):number => {
  return Math.floor(Math.random() * (max - min + 1) + min);
}
/**
 * 设置 CSS 变量的值
 * @param cssVarName CSS 变量名
 * @param value CSS 变量值
 * */
export const setCssVar = (cssVarName: string, value: string):void => {
  document.documentElement.style.setProperty(cssVarName, value);
};
/**
 * 获取CSS 变量的值
 * @param cssVarName CSS 变量名
 * */
export const getCssVar = (cssVarName:string):string=>{
  const rootStyles = getComputedStyle(document.documentElement);
  return rootStyles.getPropertyValue(cssVarName).trim();
}
/**
 * 获取cookie列表
 * */
export const getCookieList = ():{[key:string]:string}=>{
  return document.cookie.split(';').reduce((acc, cookie) => {
    const [key, value] = cookie.split('=');
    acc[key.trim()] = decodeURIComponent(value);
    return acc;
  }, {});
}





































