/**
 * 孵化器用户逻辑层 
 * 作者: 陈金晶
 * 主要包括有 孵化器账号的登录 
 * 预留好  重置密码  退出登录 接口 
 * 密码规则：6-18位 只允许有数字和字符，可以只有数字也可以只有字母 不允许有特殊字符 2023年02月21日确定需求
 */


import { ERRORCODEENUM, ERRORENUM } from "../../../config/errorEnum";
import * as fuhuaqiData from "../../../data/fuHuaQi/fuhuaqi"
import * as codeData from "../../../data/fuHuaQi/code"
import { BizError } from "../../../util/bizError";
import * as sysTools from "../../../tools/system";
import { get } from "../../../util/request";
import { systemConfig } from "../../../config/serverConfig";
import { CODETYPE } from "../../../config/enum";
import { sendChangePwdCode } from "../../sms";


/**
 * 小程序的孵化器登录
 * 小程序端
 * @param uscc 信用代码
 * @param pwd 密码
 * @returns fuhuaqiUserInfo:{uscc, name} 登录后的信息
 */
export async function login(uscc:string, pwd:string) {
    if (!sysTools.eccUscc(uscc)) throw new BizError(ERRORENUM.统一社会信用代码不合法, '孵化器登录时');
    let fuhuaqiInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);
    if(!fuhuaqiInfo) throw new BizError(ERRORENUM.账号不存在);

    let checkPwd = sysTools.getPwdMd5(fuhuaqiInfo.uscc, pwd);
    if (fuhuaqiInfo.pwd != checkPwd) throw new BizError(ERRORENUM.密码错误);

    const Token = sysTools.getToken(uscc);
    
    let fuhuaqiUserInfo = {
        uscc: fuhuaqiInfo.uscc,
        // name: fuhuaqiInfo.name,
        firstLogin : !fuhuaqiInfo.firstLoginIsChangePwd,
        token:Token
    };

    fuhuaqiInfo.token = Token;
    fuhuaqiInfo.tokenMs = new Date().valueOf();

    await fuhuaqiInfo.save();
    
    return fuhuaqiUserInfo;
}


/**
 * 首次登录修改密码
 * 小程序端
 * @param uscc 孵化器统一信用代码
 * @param pwd 新密码
 * @param confirmPwd 确认密码 
 * @returns 
 */
export async function firstLoginChangePwd(uscc:string, pwd:string, confirmPwd:string ) {
    if (pwd != confirmPwd) throw new BizError(ERRORENUM.密码不一致);
    if (pwd.search(/^[A-Za-z0-9]{6,18}$/) < 0) throw new BizError(ERRORENUM.密码只能由6至18位字符和数字组成);

    let dataBaseInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);
    if (dataBaseInfo.firstLoginIsChangePwd) throw new BizError(ERRORENUM.不能重复修改密码, `重复调用了首次登录之后的修改密码接口${uscc}`);

    dataBaseInfo.pwd = sysTools.getPwdMd5(uscc, sysTools.md5PwdStr(pwd));
    dataBaseInfo.firstLoginIsChangePwd = true;
    await dataBaseInfo.save();

    return {isSuccess:true};
}


/**
 * 修改密码
 * 小程序端 2.0
 * @param uscc 信用代码
 * @param pwd 原密码  md5之后的
 * @param newPwd 新密码  未md5
 * @param confirmPwd 确认新密码  未md5
 * @returns {isSuccess:true/false}
 */
export async function changePassword(uscc:string, pwd:string, newPwd:string, confirmPwd:string ) {
    if (newPwd != confirmPwd) throw new BizError(ERRORENUM.密码不一致);
    if (newPwd.search(/^[A-Za-z0-9]{6,18}$/) < 0) throw new BizError(ERRORENUM.密码只能由6至18位字符和数字组成);

    let fuhuaqiInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);
    if (!fuhuaqiInfo) throw new BizError(ERRORENUM.未找到数据);

    /**由于pwd是md5之后的 所以这里md5一次即可 */
    let checkPwd = sysTools.getPwdMd5(fuhuaqiInfo.uscc, pwd);
    if (fuhuaqiInfo.pwd != checkPwd) throw new BizError(ERRORENUM.密码错误);

    /**考虑到如果前端把新密码也md5 就不可以验证是不是符合规则 所以前端传的是明文
     * 我们初始化密码的时候 有两次加密 第一次是密码md5 然后再和uscc 进行md5
     * 于是这里也要如此操作
     */
    fuhuaqiInfo.pwd = sysTools.getPwdMd5(uscc, sysTools.md5PwdStr(newPwd));
    await fuhuaqiInfo.save();

    return {isSuccess:true};
}


/**
 * 退出登录
 * 小程序端
 * @param uscc 信用代码
 * @returns {isSuccess:true/false}
 */
export async function logout(uscc:string){
    if (typeof uscc != "string") throw new BizError(ERRORENUM.参数错误, uscc);
    let fuhuaqiInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);

    fuhuaqiInfo.token = "";
    fuhuaqiInfo.tokenMs = 0;

    await fuhuaqiInfo.save();
    return {isSuccess:true};
}


/**
 * 绑定账号
 * 小程序端 2.0
 * 说明： 一个账号只能被一个群体绑定  例如：A账号被B绑定着，今天被C绑定了，
 * @param uscc 发起账号的统一信用代码
 * @param bindUscc 被绑定账号统一信用代码
 * @param bindPwd 被绑定账号密码
 * @param deviceId 绑定时的设备号id
 */
export async function bindFuHuaQi(uscc:string, bindUscc:string, bindPwd:string, code:string) {
    /**获取微信的openid */
    let openId = await getOpenId(code);

    /**校验被绑定账号存不存在 */
    let bindFuhuaqiInfo = await fuhuaqiData.findFuHuaQiByUSCC(bindUscc);
    if (!bindFuhuaqiInfo) throw new BizError(ERRORENUM.账号不存在, `绑定账号时 =${bindUscc}=`);

    /**校验被绑定账号密码是否正确 */
    let checkBindPwd = sysTools.getPwdMd5(bindFuhuaqiInfo.uscc, bindPwd);
    if (bindFuhuaqiInfo.pwd != checkBindPwd) throw new BizError(ERRORENUM.密码错误);

    /**发起账号信息 */
    let fuhuaqiInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);

    /**如果操作账号从来没有绑定行为 则生成一个绑定id  否则沿用*/
    const BindId = !fuhuaqiInfo.bindId ? sysTools.getBindId(uscc, bindUscc) : fuhuaqiInfo.bindId;
    
    bindFuhuaqiInfo.bindId = BindId;
    bindFuhuaqiInfo.bindDeviceId = openId;
    await bindFuhuaqiInfo.save();

    /**直接覆盖发起账号的设备信息 */
    fuhuaqiInfo.bindDeviceId = openId;
    fuhuaqiInfo.bindId = BindId;
    await fuhuaqiInfo.save();

    return {isSuccess:true};
}


/**
 * 已绑定账号列表
 * 小程序端 2.0
 * @param uscc 孵化器统一信用代码
 * @param deviceId 当前设备号id
 */
export async function bindUserList(uscc:string, code:string) {
     /**获取微信的openid */
     let openId = await getOpenId(code);
    
    /**发起账号信息 */
    let fuhuaqiInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);

    /**
     * 找绑定关系
     * 原始需求：更换设备绑定之后，换回以前的设备是无法看到绑定关系的
     *  */
    let bindList = [];
    if (fuhuaqiInfo.bindId && fuhuaqiInfo.bindDeviceId && openId == fuhuaqiInfo.bindDeviceId ) {
        let checkList = await fuhuaqiData.findFuHuaQiList({ bindId:fuhuaqiInfo.bindId, bindDeviceId:openId });
        checkList.forEach(info => {
            let currentAccount = info.uscc == fuhuaqiInfo.uscc;
            bindList.push({
                uscc:info.uscc,
                currentAccount,
                personInChargePhone:info.personInChargePhone,
                operationName:info.operationName
            });
        });
    }

    return {bindList}
}


/**
 * 切换账号
 * @param uscc 发起账号统一信用代码
 * @param targetUscc 目标账号统一信用代码
 */
export async function changeUser(uscc:string, targetUscc:string) {
    /**发起账号信息 */
    let fuhuaqiInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);

    /**目标账号信息 */
    let targetFuhuaqiInfo = await fuhuaqiData.findFuHuaQiByUSCC(targetUscc);
    if (!targetFuhuaqiInfo) throw new BizError(ERRORENUM.账号不存在, `切换账号时=${targetUscc}=`);

    if (fuhuaqiInfo.bindId != targetFuhuaqiInfo.bindId || targetFuhuaqiInfo.bindDeviceId != fuhuaqiInfo.bindDeviceId) {
        throw new BizError(ERRORENUM.账号非绑定关系, `发起账号：${uscc}  目标账号：${targetUscc}`);
    }

    const Token = sysTools.getToken(uscc+targetUscc);

    targetFuhuaqiInfo.token = Token;
    targetFuhuaqiInfo.tokenMs = new Date().valueOf();

    await targetFuhuaqiInfo.save();

    return {
        uscc: targetFuhuaqiInfo.uscc,
        firstLogin : !targetFuhuaqiInfo.firstLoginIsChangePwd,
        token:Token
    };
}

async function getOpenId(code:string) {
    let param = {
        js_code:code,
        secret:systemConfig.secret,
        appid:systemConfig.appId,
        grant_type:"authorization_code"
    };
    let weixinRes:any = await get(systemConfig.getOpenIdUrl, param);
    if (!weixinRes) throw new BizError(ERRORENUM.code无效, code);
    if (weixinRes.errcode == ERRORCODEENUM.code无效) throw new BizError(ERRORENUM.code无效, code);
    if (weixinRes.errcode == ERRORCODEENUM.频繁操作请稍后再试) throw new BizError(ERRORENUM.频繁操作请稍后再试, code);
    if (weixinRes.errcode == ERRORCODEENUM.高风险等级用户) throw new BizError(ERRORENUM.高风险等级用户, code);
    if (weixinRes.errcode == -1) throw new BizError(ERRORENUM.系统繁忙, code);
    if (!weixinRes.openid) throw new BizError(ERRORENUM.绑定失败, code);
    
    return weixinRes.openid;
}

/**
 * 发送修改密码的短信验证码
 * @param uscc 孵化器统一信用代码
 */
export async function changePwdSendCode(uscc:string, phone:string) {
    if (!sysTools.eccUscc(uscc)) throw new BizError(ERRORENUM.统一社会信用代码不合法, '重置密码时');
    let fuHuaQiInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);
    if (!fuHuaQiInfo) throw new BizError(ERRORENUM.账号不存在, `发送验证码时 uscc:${uscc}`);
    if (!fuHuaQiInfo.personInChargePhone) throw new BizError(ERRORENUM.没有联系人, '修改密码发验证码时');
    if ( phone != fuHuaQiInfo.personInChargePhone) throw new BizError(ERRORENUM.号码与主体不一致, '修改密码发验证码时');

    let todayMs = sysTools.getTodayMs();
    let todayCodeList = await codeData.findTodayCodeByUscc(uscc, todayMs);
    if (todayCodeList.length >= 4) throw new BizError(ERRORENUM.发送验证码次数超限制, `${uscc} 修改密码发送验证码次数超限制4`);

    let sendMs = todayMs;
    todayCodeList.forEach(info => {
        sendMs = Math.max(sendMs, info.sendMs);
    });

    let now = new Date().valueOf();
    if ((now - sendMs) <= (60 * 1000) ) throw new BizError(ERRORENUM.发送验证码频率过快, `${uscc}`);

    let codeId = sysTools.getSMSCodeId(uscc, todayCodeList.length);
    let code = sysTools.getSMSCode();

    
    await sendChangePwdCode(phone, code);
    now = new Date().valueOf();

    await codeData.createCode(uscc, codeId, code, CODETYPE.修改密码, now);

    return {
        isSuccess:true,
        sendMs:now
    };
}


/**
 * 重置密码
 * @param phone 
 * @param uscc 
 * @param code 
 * @param pwd 
 * @param confirmPwd 
 */
export async function resettingPwd(phone:string, uscc:string, code:string, pwd:string, confirmPwd:string) {
    if (!sysTools.eccUscc(uscc)) throw new BizError(ERRORENUM.统一社会信用代码不合法, '重置密码时');
    let fuHuaQiInfo = await fuhuaqiData.findFuHuaQiByUSCC(uscc);
    if (!fuHuaQiInfo) throw new BizError(ERRORENUM.账号不存在, `重置密码时 uscc:${uscc}`);
    if ( phone != fuHuaQiInfo.personInChargePhone) throw new BizError(ERRORENUM.号码与主体不一致, '修改密码发验证码时');
    if (pwd != confirmPwd) throw new BizError(ERRORENUM.密码不一致);
    if (pwd.search(/^[A-Za-z0-9]{6,18}$/) < 0) throw new BizError(ERRORENUM.密码只能由6至18位字符和数字组成);

    let todayMs = sysTools.getTodayMs();
    let codeList = await codeData.findTodayCodeByUscc(uscc, todayMs);

    let now = new Date().valueOf();

    let codeId = '';
    let msg = ERRORENUM.验证码错误;
    codeList.forEach(info => {
        if (info.code == code) {
            if (info.isUse) msg = ERRORENUM.验证码失效;
            else if ( (now - info.sendMs) > (30 * 60 * 1000) ) msg = ERRORENUM.验证码过期
            else codeId = info.id;
        }
    });

    if (!codeId) throw new BizError(msg, `uscc:${uscc}重置密码的code:${code}`);

    await codeData.updateCodeState(codeId);

    fuHuaQiInfo.pwd = sysTools.getPwdMd5(uscc, sysTools.md5PwdStr(pwd));

    await fuHuaQiInfo.save();

    return {isSuccess:true};
}