package com.jeequan.jeepay.mch.ctrl.anon;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.bizcommons.manage.auth.AuthByQrcodeManage;
import com.jeequan.jeepay.bizcommons.manage.sms.SmsManager;
import com.jeequan.jeepay.core.aop.MethodLog;
import com.jeequan.jeepay.core.cache.RedisUtil;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.jwt.JWTPayload;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.core.utils.JsonKit;
import com.jeequan.jeepay.db.entity.SysUserAuth;
import com.jeequan.jeepay.mch.ctrl.CommonCtrl;
import com.jeequan.jeepay.mch.service.AuthService;
import com.jeequan.jeepay.service.impl.SysUserAuthService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;

/**
 * 登录鉴权
 *
 * @author terrfly
 * @date 2021-04-27 15:50
 */
@RestController
@RequestMapping("/api/anon/auth")
public class AuthController extends CommonCtrl {

    @Autowired
    private AuthService authService;
    @Autowired
    private SmsManager smsManager;
    @Autowired
    private SysUserAuthService sysUserAuthService;
    @Autowired
    private UserDetailsService jeeUserDetailsServiceImpl;

    /**
     * 这里用于内部接口调用, 不要开放外部登录
     * @return
     */
    @PostMapping("/privateValidate")
    @MethodLog(remark = "三方接口获取accessToken")
    public ApiRes privateValidate() {
        String userId = getValStringRequired("loginName");
        String pwd = getValStringRequired("pwd");
        SysUserAuth userAuth = sysUserAuthService.lambdaQuery()
                .eq(SysUserAuth::getIdentifier, userId)
                .eq(SysUserAuth::getCredential, pwd)
                .eq(SysUserAuth::getSysType, CS.SYS_ROLE_TYPE.MCH)
                .one();

        Assert.notNull("用户ID或密码错误");

        String loginPageType = JWTPayload.LOGIN_PAGE_TYPE.LITE;

        UserDetails userDetails = jeeUserDetailsServiceImpl.loadUserByUsername(userAuth.getIdentifier());

        String accessToken = authService.userDetailsProcess(userDetails, loginPageType, null, JWTPayload.CREDENTIAL_AUTH_TYPE.PASSWD, null);

        return ApiRes.ok(accessToken);
    }

    /**
     * 用户信息认证 获取iToken
     * <p>
     * 认证流程：
     * <p>
     * WEB端：
     * 1. 要求输入（用户名 、 密码、 图形验证码）必填， 此时若登录失败会有防穷举校验。
     * 2. 若绑定了MFA并且第一步验证通过返回：  {isMFA: true}
     * 3. 进入到二次验证逻辑， 要求输入（用户名 、 密码、  MFACode） ,  正常逻辑： 用户名密码已经验证过了， 此时不会有防穷举的验证。  会直接进入到MFA验证过程。
     * <p>
     * APP: 不验证图形， 其他不变。
     */
    @RequestMapping(value = "/validate", method = RequestMethod.POST)
    @MethodLog(remark = "登录认证")
    public ApiRes validate() throws BizException {

        String loginPageType = Base64.decodeStr(getValStringRequired("lt"));    //登录页面（终端）类型,  已做base64处理

        // 是否app登录
        boolean isApp = loginPageType.equals(JWTPayload.LOGIN_PAGE_TYPE.APP) || loginPageType.equals(JWTPayload.LOGIN_PAGE_TYPE.LITE)
                || loginPageType.equals(JWTPayload.LOGIN_PAGE_TYPE.FACE_APP);

        String account = Base64.decodeStr(getValStringRequired("ia"));  //用户名 i account, 已做base64处理
        String ipassport = Base64.decodeStr(getValStringRequired("ip"));    //密码 i passport,  已做base64处理

        String vercode = isApp ? "" : Base64.decodeStr(getValStringRequired("vc"));    // 验证码 vercode,  已做base64处理
        String vercodeToken = isApp ? "" : Base64.decodeStr(getValStringRequired("vt"));    //验证码token, vercode token ,  已做base64处理

        String mfaCode = StringUtils.isEmpty(getValString("mc")) ? null : Base64.decodeStr(getValString("mc"));    // MFACode,  已做base64处理

        // 验证码校验: WEB登录 & 第一次认证（MFACode为空的情况）
        if (!isApp && StringUtils.isEmpty(mfaCode)) {
            String cacheCode = RedisUtil.getString(CS.getCacheKeyImgCode(vercodeToken));
            if (StringUtils.isEmpty(cacheCode) || !cacheCode.equalsIgnoreCase(vercode)) {
                throw new BizException("验证码有误！");
            }
        }

        // 返回前端 accessToken
        return authService.authByAuthentication(account, ipassport, loginPageType, vercodeToken, mfaCode, request.getHeader(CS.FACE_DEVICE_NAME));
    }

    /**
     * 用户信息认证[手机验证码登录] 获取iToken
     **/
    @RequestMapping(value = "/phoneCode", method = RequestMethod.POST)
    @MethodLog(remark = "手机验证码登录")
    public ApiRes phoneCode() throws BizException {

        String loginPageType = Base64.decodeStr(getValStringRequired("lt"));    //登录页面（终端）类型,  已做base64处理
        String phone = Base64.decodeStr(getValStringRequired("phone"));    // 手机号,  已做base64处理
        String code = Base64.decodeStr(getValStringRequired("code"));    // 验证码 code,  已做base64处理

        // 校验验证码 && 如果有异常 抛异常
        smsManager.checkSmsVercodeThrowBizEx(phone, code, CS.SMS_TYPE_API_ENUM.TYPE_AUTH);

        // 返回前端 accessToken
        return authService.authByPhoneCode(phone, loginPageType);

    }

    /**
     * 图片验证码
     **/
    @RequestMapping(value = "/vercode", method = RequestMethod.GET)
    public ApiRes vercode() throws BizException {

        //定义图形验证码的长和宽 // 4位验证码
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(137, 40, 4, 80);
        lineCaptcha.createCode(); //生成code

        //redis
        String vercodeToken = UUID.fastUUID().toString();
        RedisUtil.setString(CS.getCacheKeyImgCode(vercodeToken), lineCaptcha.getCode(), CS.VERCODE_CACHE_TIME); //图片验证码缓存时间: 1分钟

        JSONObject result = new JSONObject();
        result.put("imageBase64Data", lineCaptcha.getImageBase64Data());
        result.put("vercodeToken", vercodeToken);
        result.put("expireTime", CS.VERCODE_CACHE_TIME);

        return ApiRes.ok(result);
    }

    /**
     * 轮询  登录二维码状态
     **/
    @GetMapping("/qrcodeStatus")
    public ApiRes qrcodeStatus() {

        String qrcodeNo = getValString("qrcodeNo");

        // 未传入qrcodeNo，直接返回一个，并存入redis，key：LOGIN_QRCODE_qrcodeNo
        if (StringUtils.isBlank(qrcodeNo)) {
            qrcodeNo = CS.getCacheKeyLoginQrcode(IdUtil.simpleUUID());
            AuthByQrcodeManage.setQrcodeStatusWaiting(qrcodeNo);
            return ApiRes.ok(JsonKit.newJson("qrcodeNo", qrcodeNo));
        }

        // 根据传入的qrcodeNo，判断二维码被扫状态
        String qrCodeStatus = RedisUtil.getString(qrcodeNo);

        // redis未查询到qrcodeNo信息
        if (StringUtils.isBlank(qrCodeStatus)) {
            return ApiRes.ok(JsonKit.newJson("qrcodeStatus", AuthByQrcodeManage.QRCODE_STATUS_EXPRIED));
        }

        JSONObject resJSON = new JSONObject();

        if (qrCodeStatus.startsWith(AuthByQrcodeManage.QRCODE_STATUS_CONFIRMED + "_")) {

            String tokenStr = qrCodeStatus.replaceFirst(AuthByQrcodeManage.QRCODE_STATUS_CONFIRMED + "_", "");
            resJSON = JSONObject.parseObject(tokenStr);
            resJSON.put("qrcodeStatus", AuthByQrcodeManage.QRCODE_STATUS_CONFIRMED);

            // 清空二维码缓存
            AuthByQrcodeManage.delQrcodeStatus(qrcodeNo);

        } else if (qrCodeStatus.equals(AuthByQrcodeManage.QRCODE_STATUS_CANCELED)) {

            resJSON.put("qrcodeStatus", AuthByQrcodeManage.QRCODE_STATUS_CANCELED);

            // 清空二维码缓存
            AuthByQrcodeManage.delQrcodeStatus(qrcodeNo);

        } else {
            resJSON.put("qrcodeStatus", qrCodeStatus);
        }

        return ApiRes.ok(resJSON);
    }

}
