import { Inject, Provide } from '@midwayjs/decorator';
import { Context } from '@midwayjs/koa';
import * as ipdb from 'ipip-ipdb';
import * as _ from 'lodash';
import * as moment from 'moment';
import * as NodeRSA from 'node-rsa';
import * as crypto from 'crypto'
import * as  speakeasy from 'speakeasy';
import { BinaryToTextEncoding } from 'crypto';
/**
 * 帮助类
 */
@Provide()
export class Utils {
  @Inject()
  baseDir;

  /**
   * 获得请求IP
   */
  async getReqIP(ctx: Context) {
    const req = ctx.req;
    return (
      req.headers['x-forwarded-for'] ||
      req.socket.remoteAddress.replace('::ffff:', '')
    );
  }

  /**
   * 根据IP获得请求地址
   * @param ip 为空时则为当前请求的IP地址
   */
  async getIpAddr(ctx: Context, ip?: string | string[]) {
    try {
      if (!ip) {
        ip = await this.getReqIP(ctx);
      }
      const bst = new ipdb.BaseStation(`${this.baseDir}/comm/ipipfree.ipdb`);
      const result = bst.findInfo(ip, 'CN');
      const addArr: any = [];
      if (result) {
        addArr.push(result.countryName);
        addArr.push(result.regionName);
        addArr.push(result.cityName);
        return _.uniq(addArr).join('');
      }
    } catch (err) {
      return '无法获取地址信息';
    }
  }

  /**
   * 去除对象的空值属性
   * @param obj
   */
  async removeEmptyP(obj) {
    Object.keys(obj).forEach(key => {
      if (obj[key] === null || obj[key] === '' || obj[key] === 'undefined') {
        delete obj[key];
      }
    });
  }

  /**
   * 线程阻塞毫秒数
   * @param ms
   */
  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * 获得最近几天的日期集合
   * @param recently
   */
  getRecentlyDates(recently, format = 'YYYY-MM-DD') {
    moment.locale('zh-cn');
    const dates = [];
    for (let i = 0; i < recently; i++) {
      dates.push(moment().subtract(i, 'days').format(format));
    }
    return dates.reverse();
  }
  /**
   * 获得最近几个月的月数
   * @param recently
   */
  getRecentlyMonths(recently, format = 'YYYY-MM') {
    moment.locale('zh-cn');
    const dates = [];
    const date = moment(Date.now()).format('YYYY-MM');
    for (let i = 0; i < recently; i++) {
      dates.push(moment(date).subtract(i, 'months').format(format));
    }
    return dates.reverse();
  }

  /**
   * 根据开始和结束时间，获得时间段内的日期集合
   * @param start
   * @param end
   */
  getBetweenDays(start, end, format = 'YYYY-MM-DD') {
    moment.locale('zh-cn');
    const dates = [];
    const startTime = moment(start).format(format);
    const endTime = moment(end).format(format);
    const days = moment(endTime).diff(moment(startTime), 'days');
    for (let i = 0; i <= days; i++) {
      dates.push(moment(startTime).add(i, 'days').format(format));
    }
    return dates;
  }

  /**
   * 根据开始和结束时间，获得时间段内的月份集合
   * @param start
   * @param end
   */
  getBetweenMonths(start, end, format = 'YYYY-MM') {
    moment.locale('zh-cn');
    const dates = [];
    const startTime = moment(start).format(format);
    const endTime = moment(end).format(format);
    const months = moment(endTime).diff(moment(startTime), 'months');
    for (let i = 0; i <= months; i++) {
      dates.push(moment(startTime).add(i, 'months').format(format));
    }
    return dates;
  }

  /**
   * 根据开始和结束时间，获得时间段内的小时集合
   * @param start
   * @param end
   */
  getBetweenHours(start, end, format = 'YYYY-MM-DD HH') {
    moment.locale('zh-cn');
    const dates = [];
    const startTime = moment(start).format(format);
    const endTime = moment(end).format(format);
    const hours = moment(endTime).diff(moment(startTime), 'hours');
    for (let i = 0; i <= hours; i++) {
      dates.push(moment(startTime).add(i, 'hours').format(format));
    }
    return dates;
  }

  /**
   * 字段转驼峰法
   * @param obj
   * @returns
   */
  toCamelCase(obj) {
    let camelCaseObject = {};
    for (let i in obj) {
      let camelCase = i.replace(/([-_][a-z])/gi, $1 => {
        return $1.toUpperCase().replace('-', '').replace('_', '');
      });
      camelCaseObject[camelCase] = obj[i];
    }
    return camelCaseObject;
  }

  createOrderNo(prefix) {
    return (
      prefix +
      moment().format('YYYYMMDDHHmmss') +
      _.sampleSize([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4).join('')
    );
  }

  signSort(args) {
    let keys = Object.keys(args);
    keys = keys.sort();
    let newArgs = {};
    keys.forEach(key => {
      newArgs[key] = args[key];
    });

    let string = '';
    for (let k in newArgs) {
      string += '&' + k + '=' + newArgs[k];
    }
    string = string.substr(1);
    return string;
  }

  isMerchant(roleIds) {
    return roleIds.findIndex(item => +item === 2) > -1;
  }

  isAgent(roleIds) {
    return roleIds.findIndex(item => +item === 4) > -1;
  }

  stringToHex(str) {
    let hex = '';
    for (let i = 0; i < str.length; i++) {
      hex += str.charCodeAt(i).toString(16);
    }
    return hex;
  }

  hexToString(hexString) {
    // 移除前缀0x，如果有的话
    if (hexString.startsWith('0x')) {
      hexString = hexString.slice(2);
    }

    // 将16进制字符串转换为Buffer
    const buffer = Buffer.from(hexString, 'hex');

    // 将Buffer转换为字符串
    return buffer.toString('utf8');
  }

  signByMD5WithRSA(data, privateKey) {
    const hmac = crypto.createSign('md5WithRSAEncryption');
    // 更新hash对象与传入的数据
    hmac.update(data);
    // 生成哈希值
    return hmac.sign(privateKey, 'base64');
  }

  decryptByMD5WithRSA(data, privateKey) {
    return crypto.privateDecrypt({
      key: privateKey,
      padding: crypto.constants.RSA_PKCS1_PADDING
    }, data);
  }

  verifyByMD5WithRSA(data, sign, publicKey) {
    console.log(data, sign, publicKey)
    const hmac = crypto.createVerify('md5WithRSAEncryption');
    hmac.update(data);
    return hmac.verify(publicKey, sign, 'base64');
  }

  signBySha256(data, secretKey, format: BinaryToTextEncoding = 'hex') {
    const hmac = crypto.createHmac('sha256', secretKey);
    // 更新hash对象与传入的数据
    hmac.update(data);
    // 生成哈希值
    return hmac.digest(format);
  }

  signByHmacSha1(data, secretKey) {
    const hmac = crypto.createHmac('sha1', secretKey);
    // 更新hash对象与传入的数据
    hmac.update(data);
    // 生成哈希值
    return hmac.digest('base64');
  }

  getGoogleSecret() {
    return speakeasy.generateSecret({ length: 20 })
  }

  validGoogleSecret(secret, code) {
    return speakeasy.totp.verify({
      secret: secret,
      encoding: 'base32',
      token: code,
      algorithm: 'sha1'
    })
  }
}
