'use strict';
const XML2JS = require('xml2js');
const crypto = require('crypto');

module.exports = {

  // 获取 Token
  get_jwt() {
    const { ctx } = this;
    const bearerToken = ctx.request.header.authorization;
    if (!bearerToken) {
      ctx.failed('error auth');
    }
    return bearerToken && bearerToken.replace('Bearer ', '');
  },

  // 校验 Token
  async verify_token(ctx) {
    const token = this.get_jwt(ctx);
    const decode_res = await ctx.service.jwt.decode_token(token);
    const token_black = await this.app.memcache.get('auth_token_' + decode_res.data.user_uuid);
    if (token_black == token) {
      ctx.failed('jwt校验失败');
    }
    ctx.setUserUuid(decode_res.data.user_uuid);
    ctx.setOpenId(decode_res.data.openid);
    return decode_res;
  },

  md5(str) {
    if (!str) {
      return '';
    }
    const crypto = require('crypto');
    const hash = crypto.createHash('md5');
    const ret = hash.update(String(str), 'utf8');
    return ret.digest('hex');
  },

  // 发送请求 注意params和options都为对象
  async send_request(url, params, input_options) {
    const { ctx } = this;

    /** *
    example_options = {
        //是否开启请求各阶段的时间测量
    timing: false,

    //method
    method: 'POST',

    //发送数据格式，默认以application/x-www-form-urlencoded格式发送请求
    contentType: 'json',

    //参数
    data: {
        id: 5,
        name: 'test',
    }

    //设置响应数据格式，默认不对响应数据做任何处理，直接返回原始的 buffer 格式数据。 支持 text 和 json 两种格式。
        //注意：设置成 json 时，如果响应数据解析失败会抛 JSONResponseFormatError 异常
        dataType: 'json',
    }
    ***/

    const default_options = {
      timeout: 3000,
      timing: true,
      method: 'POST',
      contentType: 'json',
      dataType: 'json',
    };

    const options = input_options ? Object.assign({}, default_options, input_options) : default_options;

    options.data = params;
    // 测试接口：个税获取话题列表
    // var url = 'https://b.jianbing.com/app/geshui/social/get_topics';
    const resp = await ctx.curl(url, options);
    return resp;
  },

  aes256_cbc_encrypt(key, data) {
    const iv = '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0';
    const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    let crypted = cipher.update(data, 'utf8', 'binary');
    crypted += cipher.final('binary');
    crypted = new Buffer(crypted, 'binary').toString('base64');
    return crypted;
  },

  aes256_cbc_decrypt(key, crypted) {
    const iv = '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0';
    crypted = new Buffer(crypted, 'base64').toString('binary');
    const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
    let decoded = decipher.update(crypted, 'binary', 'utf8');
    decoded += decipher.final('utf8');
    return decoded;
  },

  rsaPublicDecrypt(publicKey, encryptContent) {

    const public_key = new NodeRSA(publicKey);
    const decrypt_data = public_key.decryptPublic(encryptContent).toString('utf8');

    return decrypt_data;
  },

  rsaPrivateEncrypt(privateKey, content) {

    const private_key = new NodeRSA(privateKey);
    const encrypt_data = private_key.encryptPrivate(content, 'base64');

    return encrypt_data;
  },

  aes256_cbc_decrypt_weixin(key, crypted) {
    const aesKey = Buffer.from(key + '=', 'base64');
    const cipherEncoding = 'base64';
    const clearEncoding = 'utf8';
    const cipher = crypto.createDecipheriv('aes-256-cbc', aesKey, aesKey.slice(0, 16));
    cipher.setAutoPadding(false); // 是否取消自动填充 不取消
    const decoded = cipher.update(crypted, cipherEncoding, clearEncoding) + cipher.final(clearEncoding);
    return {
      noncestr: decoded.substring(0, 16),
      msg_len: decoded.substring(16, 20),
      msg: decoded.substring(20, decoded.lastIndexOf('}') + 1),
    };
  },

  toInt(str) {
    if (typeof str === 'number') return str;
    if (!str) return str;
    return parseInt(str, 10) || 0;
  },


  // 校验经纬度 部分存入的gps地址经纬度混乱，输出使用时进行校验修复
  // longitude 经线 latitude 纬度 纬度从南到北，范围为-90 - 90
  // 示例：北纬N29°57′28.20″ 东经E119°42′32.30″   gps:29.9578340000,119.7089730000
  checkGps(gps) {

    if (!gps || gps.indexOf(',') === -1) {
      return '';
    }
    const gps_arr = gps.split(',');
    if (Math.abs(gps_arr[0]) >= 90) {
      gps_arr.reverse();
      gps = gps_arr.join(',');
    }

    return gps;
  },


  async parseGps(gps) {

    if (!gps || gps.indexOf(',') === -1) {
      return '';
    }
    const gps_arr = gps.split(',');
    if (Math.abs(gps_arr[0]) >= 90) {
      gps_arr.reverse();
      gps = gps_arr.join(',');
    }

    const data = {
      ak: '3TBenWOhPygtFFazaR5kSibU',
      pois: 0,
      output: 'json',
      location: gps,
    };
    const { ctx } = this;
    const resp = await ctx.curl('http://api.map.baidu.com/geocoder/v2/', { timeout: 3000, dataType: 'json', method: 'GET', data });

    const ret = resp.data;
    if (ret.status != 0) {
      return {};
    }
    return ret;
  },

  async getGPS(address, city) {
    console.info(address);
    const baidu_url = 'https://api.map.baidu.com/geocoder/v2/';
    const result = await this.send_request(baidu_url, { address, output: 'json', ak: '3TBenWOhPygtFFazaR5kSibU', city }, { method: 'GET' });
    console.info(result);
    const ret = result.data;
    if (ret && ret.status === 0 && ret.result && ret.result.location) {
      return ret.result.location;
    }
    return { lng: 0, lat: 0 };
  },

  // 根据IP地址来处理业务的展示与否
  async parseIp() {
    const ip = this.getClientIP();
    //
    // ip = '122.224.130.226';

    if (!ip) {
      return '';
    }

    const data = {
      ak: '3TBenWOhPygtFFazaR5kSibU',
      output: 'json',
      ip,
    };
    const { ctx } = this;
    const resp = await ctx.curl('https://api.map.baidu.com/location/ip', { timeout: 3000, dataType: 'json', method: 'GET', data });

    const ret = resp.data;
    if (ret.status != 0) {
      return {};
    }
    return ret;
  },

  async check_submit_frequent(redis_key, expire = 5) {
    const { ctx, app } = this;
    const redis_value = await app.memcache.get(redis_key);
    if (redis_value) {
      ctx.failed('提交太频繁了，过会在试吧~');
    }
    app.memcache.set(redis_key, 1, expire);
    return true;
  },

  randomsort(a, b) { // 数组随机排序
    return Math.random() > 0.5 ? -1 : 1; // 通过随机产生0到1的数，然后判断是否大于0.5从而影响排序，产生随机性的效果。
  },

  debug(data, mark = '') {
    let str = '';
    for (let i = 0; i <= 60; i++) {
      str += mark;
    }
    console.log(str);
    console.log(data);
    console.log(str);
  },

  sha1(...data) {
    if (!data) {
      return '';
    }
    const crypto = require('crypto');
    return crypto.createHash('sha1').update(data.sort().join('')).digest('hex');
  },

  unique(arr) {
    const res = arr.filter(function(item, index, array) {
      return array.indexOf(item) === index;
    });
    return res;
  },

  // 校验身份证
  verify_id_card(id_card) {
    const id_card_reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
    if (id_card_reg.test(id_card) === false) { // 身份证号码校验
      return false;
    }
    /* 1、从第一位到第十七位的系数分别为：
      7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2
      将这17位数字和系数相乘的结果相加。 */
    const arr = [ 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 ];
    let sum = 0;
    for (let i = 0; i < arr.length; i++) {
      sum += parseInt(id_card.charAt(i)) * arr[i];
    }
    // 2、用加出来和除以11，看余数，
    const c = sum % 11;
    // 3、分别对应的最后一位身份证的号码为：1－0－X－9－8－7－6－5－4－3－2
    const ch = [ '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' ];
    const code = ch[c];
    let last = id_card.charAt(17);
    last = last == 'x' ? 'X' : last;
    return last == code;
  },

  // 随机化原数组
  shuffle(array) {
    let m = array.length,
      t,
      i;
    // 如果还剩有元素…
    while (m) {
      // 随机选取一个元素…
      i = Math.floor(Math.random() * m--);
      // 与当前元素进行交换
      t = array[m];
      array[m] = array[i];
      array[i] = t;
    }
    return array;
  },

  // 获取客户端IP
  getClientIP() {
    const { ctx } = this;
    const ips = ctx.request.header['x-forwarded-for'];
    const ipList = ips ? ips.split(',') : [ '' ];
    const ip = ipList[0];
    return ip;
  },

  /**
     * @author lisk
     * @description 生成随机字串
     * @param {*} length
     */
  createNoncestr(length = 32) {

    const chars = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    const clen = chars.length;
    let str = '';
    for (let i = 0; i < length; i++) {
      str += chars.substr(parseInt(Math.random() * clen) - 1, 1);
    }

    return str;
  },

  JsonParse(jsonStr) {
    try {
      return JSON.parse(jsonStr);
    } catch (error) {
      return [];
    }
  },

  /** 判断是否是手机号**/
  isPhoneNumber(tel) {
    const reg = /^0?1[3|4|5|6|7|8|9][0-9]\d{8}$/;
    return reg.test(tel);
  },

  // 0姓名格式不正确；-1至少输入2个汉字 1成功
  verify_real_name(real_name) {

    const real_name_reg = /^[\u4E00-\u9FA5]{1,10}$/;
    if (!real_name_reg.test(real_name)) { // 姓名校验
      return 0;
    }
    if (real_name.length < 2) {
      return -1;
    }

    return 1;
  },
  // 固定位数前补充 0 操作
  PrefixInteger(num, length) {
    return (Array(length).join('0') + num).slice(-length);
  },

  async xmlTojson(data) {
    const ret_p = new Promise((resolve, reject) => {
      XML2JS.parseString(data, (err, result) => {
        if (err) {
          this.ctx.failed(err);
          reject();
        } else {
          const ret = result.xml;
          resolve(ret);
        }
      });
    });
    let ret_json = {};
    await ret_p.then((resolve, reject) => { ret_json = resolve; });
    return ret_json;
  },

  jsonToxml(data) {
    const bulider = new XML2JS.Builder();
    const xml_params = bulider.buildObject(data);
    return xml_params;
  },

  // 获取手机号码归属地
  async getPhoneCity(phone) {

    const { ctx } = this;
    const url = 'https://mobsec-dianhua.baidu.com/dianhua_api/open/location';
    let location = '--';
    const result = await ctx.helper.send_request(url, { tel: phone }, { method: 'GET', timeout: 6000 });
    if (result.status !== 200) {
      return location;
    }
    const ret = result.data;
    let city = '';
    if (ret.response && ret.response[phone] && ret.response[phone].detail && ret.response[phone].detail.area) {
      city = ret.response[phone].detail.area[0].city;
    }
    let provinece = '';
    if (ret.response && ret.response[phone] && ret.response[phone].detail && ret.response[phone].detail.province) {
      provinece = ret.response[phone].detail.province;
    }
    if (city == provinece) {
      city = '';
    }
    location = provinece + city;

    return location;
  },

  // 延时函数
  async sleep(time) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        resolve('ok');
      }, time);
    });
  },

  // 格式化地址
  formatUrl(url, baseUrl) {
    if (!baseUrl) return url;
    if (url.indexOf('http') === 0) return url;
    let formatUrl = '';
    const baseUrlEndsWithSlash = baseUrl.endsWith('/');
    const urlStartWithSlash = url.startsWith('/');
    if (baseUrlEndsWithSlash && urlStartWithSlash) {
      formatUrl = baseUrl + url.substring(1);
    } else if (baseUrlEndsWithSlash || urlStartWithSlash) {
      formatUrl = baseUrl + url;
    } else {
      formatUrl = baseUrl + '/' + url;
    }
    return formatUrl;
  },

  decodeUserSid(code) {

    if (!code) return code;
    const rand = 'OU1WjLvZCrRJ7Yo0gE2XDjuuaSAUuaH1bhHPuMymcdfEeKz0igRhXQkMuLTm1';
    code = code.slice(5, code.length);
    const begin = code.slice(0, 1);
    let rtn = '';
    const codelen = rand.slice(0, 11);
    const codenums = rand.slice(11, 23);
    let len = codelen.indexOf(begin);
    if (len > -1) {
      len++;
      const arrnums = code.slice(-len).split('');
      for (const v of arrnums) {
        rtn += String(codenums.indexOf(v));
      }
    }

    return parseInt(rtn) >> 2;
  },

  encodeUserSid(userSid) {
    const rand = 'OU1WjLvZCrRJ7Yo0gE2XDjuuaSAUuaH1bhHPuMymcdfEeKz0igRhXQkMuLTm1';
    const randKey = 2019;
    let rtn = '';
    const code = userSid << 2;
    const randLength = 11;
    const len = String(code).length;
    const codeLen = rand.slice(0, randLength);
    const codeNums = rand.slice(randLength, randLength + 12);
    const codeExt = rand.slice(randLength + 12, rand.length);

    const begin = codeLen.slice(len - 1, len);
    const ext = randLength - len - 1;
    let temp = String(Math.floor(code / randKey)).replace('.', '');
    temp = temp.slice(-ext);

    const arrExtTemp = codeExt.split('');
    const arrExt = temp.split('');
    for (const v of arrExt) {
      rtn += arrExtTemp[v];
    }

    const arrNumsTemp = codeNums.split('');
    const arrNums = String(code).split('');
    for (const v of arrNums) {
      rtn += arrNumsTemp[v];
    }

    rtn = begin + rtn;
    return rtn;
  },

  // aes加密
  async cipherByAES(data) {
    const { ctx } = this;
    const response_params = await ctx.helper.send_request(ctx.app.config.NODEV2_URL + '/user/cipher', {
      data: JSON.stringify(data),
      mode: 'aes',
      type: 'encrypt',
    }, {
      method: 'POST',
    });
    // ctx.logger.info('response_params', JSON.stringify(ctx.body));
    if (response_params.status !== 200) {
      ctx.failed('加密失败');
      return;
    }

    return { data: response_params.data.data };
  },
};
