
'use strict';

const Service = require('egg').Service;
const moment = require('moment');
const _ = require('lodash');
const TypeConfig = {
    blacklist: 1,
    callrisk: 2
}

class CommonService extends Service {

    /**
     * 
     * @param {*} type 黑名单blacklist通话callrisk
     * @param {*} id 报告自增编号(report_id)
     */
    async getReportNo(type, id) {
        if (!TypeConfig.hasOwnProperty(type)) {
            this.ctx.failed('getReportNo type undefined');
        }
        let tip = (type === 'blacklist') ? '00' : '01';
        let prefix = '51GJJ' + moment().format('YYYYMMDD') + tip;
        let suffix = String(Number(id) + 12580);
        if (suffix.length > 5) {
            let subStart = suffix.length - 5;
            suffix = suffix.substring(subStart, suffix.length + 1);
        }
        return prefix + suffix;
    }


    /**
    * 
    * @param {*} type 黑名单blacklist通话callrisk
    * @param {*} id 订单自增编号(order_id)
    */
    async getOrdertNo(type, id) {
        if (!TypeConfig.hasOwnProperty(type)) {
            this.ctx.failed('getOrdertNo type undefined');
        }
        let tip = (type === 'blacklist') ? '00' : '01';
        let prefix = '51GJJOD' + moment().format('YYYYMMDD') + tip;
        let suffix = String(Number(id) + 12580);
        if (suffix.length > 5) {
            let subStart = suffix.length - 5;
            suffix = suffix.substring(subStart, suffix.length + 1);
        }
        return prefix + suffix;
    }

    /**
     * 签名函数
     * @param {*} params 需要签名的参数
     */
    async sign(params, appSecret) {
        const { ctx } = this;
        let sign = '';
        if (!params) {
            return sign;
        }
        //键名排序
        const sortParamsKey = Object.keys(params).sort();
        //键值拼接-升序
        let sortValues = '';
        for (let i in sortParamsKey) {
            if (sortParamsKey[i] === 'sign') {
                continue;
            }
            sortValues += sortParamsKey[i] + '=' + params[sortParamsKey[i]] + '&';
        }
        sortValues += 'appSecret=' + appSecret;
        sign = await ctx.helper.md5(sortValues).toUpperCase();
        ctx.logger.info({ sign: sign });
        return sign;
    }

    async WexinUnifiedOrder(params) {

        const { ctx } = this;
        const { body, trade_no, total_fee, notify_url, product_id, scene_info, expire_time } = params;
        const appid = this.config.MCH_APPID;
        const mch_id = this.config.MCH_ID;
        const trade_type = 'MWEB';
        const nonce_str = ctx.helper.createNoncestr();
        // const time_expire = expire_time ? expire_time : moment().add(1, 'days').format('YYYYMMDDHHmmss');
        const spbill_create_ip = ctx.helper.getClientIP();
        // const spbill_create_ip = '122.224.130.226';
        let json_data = {
            appid,
            mch_id,
            body,
            out_trade_no: trade_no,
            total_fee,
            spbill_create_ip,
            notify_url,
            product_id,
            scene_info,
            trade_type,
            nonce_str,
            // time_expire,
        };
        const sign = this.getWexinSign(json_data);
        json_data.sign = sign;
        const xml_data = ctx.helper.jsonToxml(json_data);
        console.info(xml_data);
        const url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        const result_wexin = await ctx.curl(url, { timeout: 3000, method: 'POST', data: xml_data, });

        if (result_wexin.status !== 200) {
            ctx.failed('wexin pay failed');
        }
        const result = await ctx.helper.xmlTojson(result_wexin.data);
        ctx.logger.info({ pay_wexin_params: json_data, pay_wexin_result: result });
        let ret = {};
        for (let key in result) {
            ret[key] = result[key][0];
        }
        let error_msg = 'wexin pay error';
        if (!ret.return_code || ret.return_code !== 'SUCCESS') {
            error_msg = ret.return_msg || error_msg;
            ctx.failed(error_msg);
        }
        if (!ret.result_code || ret.result_code !== 'SUCCESS') {
            error_msg = ret.err_code_des || error_msg;
            ctx.failed(error_msg);
        }

        return ret;
    }

    async alipay() {//TODO

    }

    async WexinCheckPay(params) {

        const { ctx } = this;
        const { transaction_id, trade_no } = params;
        if (!transaction_id && !trade_no) {
            return false;
        }
        // const order_info = await ctx.service.credit.order.getOrderInfo(pay_info.order_id);
        const nonce_str = ctx.helper.createNoncestr();
        const url = 'https://api.mch.weixin.qq.com/pay/orderquery';
        let data_obj = {
            appid: this.config.MCH_APPID,
            mch_id: this.config.MCH_ID,
            nonce_str,
        }
        if (transaction_id) data_obj.transaction_id = transaction_id;
        if (trade_no) data_obj.out_trade_no = trade_no;
        const sign = this.getWexinSign(data_obj);
        data_obj.sign = sign;
        const xml_data = ctx.helper.jsonToxml(data_obj);
        const result_wexin = await ctx.curl(url, { timeout: 3000, method: 'POST', data: xml_data, headers: { 'content-type': 'text/html', }, streaming: false, dataType: 'text', });
        ctx.logger.info(JSON.stringify({ query_wexinpay_param: data_obj, query_wexinpay_result: result_wexin.data }));
        if (result_wexin.status !== 200) {
            ctx.failed('wexin orderquery error1');
        }
        const ret = await ctx.helper.xmlTojson(result_wexin.data);
        let ret_data = {};
        for (let key in ret) {
            ret_data[key] = ret[key][0];
        }
        // ctx.logger.info(JSON.stringify({ query_wexinpay_param: params, query_wexinpay_result: ret_data }));
        let error_msg = 'wexin orderquery error2';
        if (ret_data.return_code !== 'SUCCESS' && ret_data.result_code !== 'SUCCESS') {
            error_msg = ret_data.return_msg ? ret_data.return_msg : error_msg;
            ctx.failed(error_msg);
        }

        if (ret_data.trade_state !== 'SUCCESS') {
            return false;
        }

        return { total_fee: ret_data.cash_fee, ret_data };

    }

    /**
     * @description 支付回调通知
     * @param {*} result 
     */
    async WexinNotify(result) {

        const { ctx } = this;
        if (result && result.appid[0] === this.config.MCH_APPID) {
            let sign_data = {};
            for (let key in result) {
                sign_data[key] = result[key][0];
            }
            if (sign_data.return_code && sign_data.return_code === 'SUCCESS' && sign_data.result_code && sign_data.result_code === 'SUCCESS') {
                const wexin_return_sign = sign_data.sign;
                delete sign_data.sign;
                const sign = this.getWexinSign(sign_data);
                const out_trade_no = sign_data.out_trade_no;
                const total_fee = sign_data.total_fee;
                const pay_info = await ctx.prometheusModel.CreditPay.findOne({ where: { trade_no: out_trade_no }, order: [['id', 'desc']] });
                if (!pay_info || !pay_info.order_id) {
                    ctx.failed('error pay');
                }
                const order = await ctx.prometheusModel.CreditOrder.findOne({ where: { id: pay_info.order_id } });
                if (!order || !order.id) {
                    ctx.failed('error order');
                }
                if (order && order.pay_status === 1) {
                    return true;
                }
                const price = order.price * 100 - order.preferential_price * 100;
                if (total_fee == price && wexin_return_sign === sign) {
                    const state_time = moment().format('YYYY-MM-DD HH:mm:ss');
                    await ctx.prometheusModel.CreditPay.edit({ where: { id: pay_info.id }, params: { pay_result: JSON.stringify(result), transaction_id: sign_data.transaction_id, status: 1 } });
                    await ctx.service.credit.order.logOrder(order.id);
                    await ctx.prometheusModel.CreditOrder.update({ state: '已支付', state_time: state_time, pay_status: 1 }, { where: { id: order.id } });
                    await ctx.service.credit.order.updateOrderStateToCancel(order);//更新其他同姓名和身份证号的订单状态
                    return true;
                }
            }
        }

        return false;
    }

    /**
   * @author lisk
   * @description 参数签名
   * @param {*} params 
   */
    getWexinSign(params) {

        const { ctx, app } = this;
        let ret = [];
        for (let val of Object.keys(params).sort()) {
            ret[val] = params[val];
        }
        let str = '';
        for (let key in ret) {
            str += key + '=' + ret[key] + '&';
        }
        const mch_key = app.config.MCH_KEY;

        // console.info(str + 'key=' + mch_key);
        const sign = ctx.helper.md5(str + 'key=' + mch_key).toLocaleUpperCase();

        return sign;
    }

    /**
     * 数盒魔方三要素认证和存贮
     * @param {*} data 
     * @param {*} type 黑名单blacklist通话callrisk
     */
    async queryShuhemofang(type, data) {
        const { ctx } = this;
        if (!TypeConfig.hasOwnProperty(type)) {
            ctx.failed('未知类型的数据检测');
        }
        let need = ['name', 'id_card', 'phone'];
        for (let i in need) {
            if (!data.hasOwnProperty(need[i] || !data[need[i]])) {
                ctx.failed(need[i] + '参数缺失');
            }
        }
        let ret = await this.requestShuhemofang(data);
        let shuhemofangData = {
            type: TypeConfig[type],
            user_id: ctx.userId,
            app_user_id: ctx.appUserId,
            app_id: ctx.appId,
            app_type_id: ctx.appTypeId,
            name: data.name,
            phone: data.phone,
            id_card: data.id_card,
            status: (ret.hasOwnProperty('result') && ret.result.hasOwnProperty('data')) ? ret.result.data : ret.code,
            response: JSON.stringify(ret),
        }
        let shuhemofang = await ctx.prometheusModel.CreditShuhemofang.create(shuhemofangData);
        return shuhemofang;
    }

    /**
     * 请求数盒魔方
     * @param {*} data name,phone,id_card三要素
     */
    async requestShuhemofang(data) {
        const { ctx } = this;
        let timestamp = new Date().getTime().toString();
        let params = {
            mobile: data.phone,
            idNo: data.id_card,
            name: data.name,
            appkey: this.config.SHUHEMOFANG_APP_KEY,
            timestamp: timestamp,
            sign: await ctx.helper.md5(this.config.SHUHEMOFANG_APP_KEY + this.config.SHUHEMOFANG_APP_SECRET + timestamp),
        }
        let url = this.config.SHUHEMOFANG_URL;
        // let url = this.config.SHUHEMOFANG_URL + '?';
        // for (let i in params) {
        //     url += i + '=' + params[i] + '&';
        // }
        // url = url.substring(0, url.length - 1);
        ctx.logger.info({ shmfParams: JSON.stringify(params) });
        let resp = await ctx.helper.send_request(url, params, { method: 'POST' });
        ctx.logger.info({ resp: JSON.stringify(resp) });
        return resp.data;
    }


    /**
     * 根据数盒魔方结果提示
     * @param {*} status 数盒魔方返回状态码 
     */
    async noticeShuhemofang(status) {
        const { ctx } = this;
        //异常情况
        if (status == '2') {
            ctx.failed('手机号已实名，身份证姓名均不匹配，请重新输入');
        }
        if (status == '3') {
            ctx.failed('手机号已实名，姓名不匹配，请重新输入');
        }
        if (status == '4') {
            ctx.failed('手机号已实名，身份证不匹配，请重新输入');
        }
        //未知情况
        if (status != '1') {
            ctx.failed('认证过程中出现了一些问题，请稍后重试');
        }
    }

    /**
     * 数盒魔方校验
     * @param {*} type 黑名单(blacklist)通话(callrisk)
     * @param {object} inputParams name,phone,id_card
     */
    async shuhemofangCheck(type, inputParams) {
        const { ctx } = this;
        if (!TypeConfig.hasOwnProperty(type)) {
            ctx.failed('未知类型的数据检测');
        }
        //1:判断用户当前三要素是否已经校验过
        //2:有记录根据记录弹框,没记录判断该用户是否已经校验满5次
        let shmfFilter = {
            where: {
                user_id: ctx.userId,
                type: TypeConfig[type]
            }
        }
        let shmfList = await ctx.prometheusModel.CreditShuhemofang.findAll(shmfFilter);
        ctx.logger.info({ shmfList: JSON.stringify(shmfList) });
        let history = {
            in: false,
            detail: {}
        };
        for (let i in shmfList) {
            if (shmfList[i].name === inputParams.name && shmfList[i].phone === inputParams.phone && shmfList[i].id_card === inputParams.id_card) {
                history.in = true;
                history.detail = shmfList[i];
                break;
            }
        }
        if (history.in) {
            await this.noticeShuhemofang(history.detail.status);
        } else {
            if (shmfList.length >= 5) {
                ctx.failed('您输入的想要检测人数已超过最大限制，仅可检测输入过的检测信息');
            } else {
                let ret = await this.queryShuhemofang(type, inputParams);
                ctx.logger.info({ ret: JSON.stringify(ret) });
                await this.noticeShuhemofang(ret.status);
            }
        }
    }

    /**
     * 根据身份证获取对应的省市区
     * @param {*} idCard 身份证
     */
    async getIdCardCity(idCard) {
        const { ctx } = this;
        let ret = '--';
        let prefix3 = idCard.substring(0, 3) + '000';
        let prefix6 = idCard.substring(0, 6);
        let taskList = [
            ctx.prometheusModel.CreditIdCardCity.findOne({ where: { code: prefix3 } }),
            ctx.prometheusModel.CreditIdCardCity.findOne({ where: { code: prefix6 } }),
        ];
        let retList = await Promise.all(taskList).then(result => {
            return result;
        }).catch(error => {
            ctx.failed(error);
        });
        let provinece = retList[0];
        let city = retList[1];

        //数据获取
        if (provinece === null && city === null) {
            return ret;
        }
        //省和市
        let provineceName = provinece === null ? '' : provinece.name;
        let cityName = city === null ? '' : city.name;

        if (provineceName === cityName) {
            ret = provineceName;
        } else {
            ret = !provineceName ? (!cityName ? '--' : cityName) : (!cityName || cityName === '县') ? provineceName : (provineceName + '-' + cityName);
        }
        return ret;
    }

}

module.exports = CommonService;
