package com.qmth.sop.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.boot.core.solar.model.WxappPhoneNumber;
import com.qmth.boot.core.solar.model.WxappSession;
import com.qmth.boot.core.solar.service.SolarService;
import com.qmth.sop.business.bean.result.LoginResult;
import com.qmth.sop.business.entity.SysUser;
import com.qmth.sop.business.entity.TWxappInfo;
import com.qmth.sop.business.mapper.TWxappInfoMapper;
import com.qmth.sop.business.service.SysLogService;
import com.qmth.sop.business.service.SysUserService;
import com.qmth.sop.business.service.TWxappInfoService;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.AppSourceEnum;
import com.qmth.sop.common.enums.ExceptionResultEnum;
import com.qmth.sop.common.enums.LogTypeEnum;
import com.qmth.sop.common.util.ByteUtil;
import com.qmth.sop.common.util.ShaUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import sun.misc.BASE64Decoder;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * <p>
 * 微信信息表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2023-09-15
 */
@Service
public class TWxappInfoServiceImpl extends ServiceImpl<TWxappInfoMapper, TWxappInfo> implements TWxappInfoService {

    @Resource
    SysUserService sysUserService;

    @Resource
    SolarService solarService;

    @Resource
    TWxappInfoService tWxappInfoService;

    @Value("${wxapp.appid}")
    String wxappId;

    @Resource
    SysLogService sysLogService;

    /**
     * 微信登录
     *
     * @param loginCode
     * @return
     */
    @Override
    public LoginResult wxappLogin(String loginCode) throws NoSuchAlgorithmException {
        WxappSession wxappSession = getAuthorization(loginCode);
        String openId = ByteUtil.toHexAscii(ShaUtils.encode(wxappSession.getOpenId()));
        TWxappInfo tWxappInfo = tWxappInfoService.getOne(new QueryWrapper<TWxappInfo>().lambda().eq(TWxappInfo::getOpenId, openId));
        Optional.ofNullable(tWxappInfo).orElseThrow(() -> ExceptionResultEnum.WXAPP_OPENID_NO_DATA.exception());
        List<SysUser> sysUserList = sysUserService.list(new QueryWrapper<SysUser>().lambda().eq(SysUser::getMobileNumber, tWxappInfo.getPhone()));
        if (CollectionUtils.isEmpty(sysUserList)) {
            throw ExceptionResultEnum.ERROR.exception("对不起，您不是系统用户，无法进行登录！");
        }
        if (sysUserList.size() > 1) {
            throw ExceptionResultEnum.ERROR.exception("查出多个用户");
        }
        SysUser sysUser = sysUserList.get(0);
        if (!sysUser.getEnable()) {
            throw ExceptionResultEnum.ERROR.exception("该用户已禁用");
        }
        LoginResult result = sysUserService.login(sysUser.getPassword(), sysUser, AppSourceEnum.WX_APP);
        try {
            sysLogService.saveLogWithNoAuth(sysUser.getId(), "用户登录", LogTypeEnum.LOGIN);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return result;
    }

    /**
     * 绑定微信手机号并登录（新api）
     *
     * @param loginCode
     * @param phoneCode
     * @return
     */
    @Override
    @Transactional
    public LoginResult wxappLoginByPhoneCode(String loginCode, String phoneCode) throws NoSuchAlgorithmException {
        Optional.ofNullable(wxappId).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未配置微信小程序appid"));

        WxappPhoneNumber wxappPhoneNumber = solarService.getWxappPhoneNumber(wxappId, phoneCode);
        Optional.ofNullable(wxappPhoneNumber).orElseThrow(() -> ExceptionResultEnum.WXAPP_PHONE_NO_DATA.exception());

        List<SysUser> sysUserList = sysUserService.list(new QueryWrapper<SysUser>().lambda().eq(SysUser::getMobileNumber, wxappPhoneNumber.getPurePhoneNumber()));
        if (CollectionUtils.isEmpty(sysUserList)) {
            throw ExceptionResultEnum.ERROR.exception("对不起，您不是系统用户，无法进行登录！");
        }
        if (sysUserList.size() > 1) {
            throw ExceptionResultEnum.ERROR.exception("查出多个用户");
        }
        SysUser sysUser = sysUserList.get(0);
        if (!sysUser.getEnable()) {
            throw ExceptionResultEnum.ERROR.exception("该用户已禁用");
        }
        WxappSession wxappSession = getAuthorization(loginCode);
        String openId = ByteUtil.toHexAscii(ShaUtils.encode(wxappSession.getOpenId()));
        TWxappInfo tWxappInfo = tWxappInfoService.getOne(new QueryWrapper<TWxappInfo>().lambda().eq(TWxappInfo::getOpenId, openId));
        tWxappInfo = Objects.isNull(tWxappInfo) ? new TWxappInfo(openId, wxappPhoneNumber.getPurePhoneNumber()) : tWxappInfo;
        tWxappInfoService.saveOrUpdate(tWxappInfo);
        LoginResult result = sysUserService.login(sysUser.getPassword(), sysUser, AppSourceEnum.WX_APP);
        try {
            sysLogService.saveLogWithNoAuth(sysUser.getId(), "用户登录", LogTypeEnum.LOGIN);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return result;
    }

    /**
     * 绑定微信手机号并登录（旧api）
     *
     * @param loginCode
     * @param encryptedData
     * @param iv
     * @return
     */
    @Override
    @Transactional
    public LoginResult wxappLoginByEncryptedData(String loginCode, String encryptedData, String iv) throws NoSuchAlgorithmException {
        WxappSession wxappSession = getAuthorization(loginCode);
        String openId = ByteUtil.toHexAscii(ShaUtils.encode(wxappSession.getOpenId()));
        JSONObject jo = decrypt(encryptedData, iv, wxappSession.getSessionKey());
        String phone = jo.getString(SystemConstant.PURE_PHONE_NUMBER);
        List<SysUser> sysUserList = sysUserService.list(new QueryWrapper<SysUser>().lambda().eq(SysUser::getMobileNumber, phone));
        if (CollectionUtils.isEmpty(sysUserList)) {
            throw ExceptionResultEnum.ERROR.exception("对不起，您不是系统用户，无法进行登录！");
        }
        if (sysUserList.size() > 1) {
            throw ExceptionResultEnum.ERROR.exception("查出多个用户");
        }
        SysUser sysUser = sysUserList.get(0);
        if (!sysUser.getEnable()) {
            throw ExceptionResultEnum.ERROR.exception("该用户已禁用");
        }
        TWxappInfo tWxappInfo = tWxappInfoService.getOne(new QueryWrapper<TWxappInfo>().lambda().eq(TWxappInfo::getOpenId, openId));
        tWxappInfo = Objects.isNull(tWxappInfo) ? new TWxappInfo(openId, phone) : tWxappInfo;
        tWxappInfoService.saveOrUpdate(tWxappInfo);
        LoginResult result = sysUserService.login(sysUser.getPassword(), sysUser, AppSourceEnum.WX_APP);
        try {
            sysLogService.saveLogWithNoAuth(sysUser.getId(), "用户登录", LogTypeEnum.LOGIN);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return result;
    }

    /**
     * 解密
     *
     * @param encryptedData
     * @param iv
     * @param sessionKey
     * @return
     */
    private JSONObject decrypt(String encryptedData, String iv, String sessionKey) {
        String jsonStr = null;
        try {
            BASE64Decoder base64Decoder = new BASE64Decoder();
            /**
             * 小程序加密数据解密算法
             * https://developers.weixin.qq.com/miniprogram/dev/api/signature.html#wxchecksessionobject
             * 1.对称解密的目标密文为 Base64_Decode(encryptedData)。 2.对称解密秘钥 aeskey =
             * Base64_Decode(session_key), aeskey 是16字节。 3.对称解密算法初始向量
             * 为Base64_Decode(iv)，其中iv由数据接口返回。
             */
            byte[] encryptedByte = base64Decoder.decodeBuffer(encryptedData);
            byte[] sessionKeyByte = base64Decoder.decodeBuffer(sessionKey);
            byte[] ivByte = base64Decoder.decodeBuffer(iv);
            /**
             * 以下为AES-128-CBC解密算法
             */
            SecretKeySpec skeySpec = new SecretKeySpec(sessionKeyByte, SystemConstant.AES);
            Cipher cipher = Cipher.getInstance(SystemConstant.AES_CBC_PKCS5);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(ivByte);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
            byte[] original = cipher.doFinal(encryptedByte);
            jsonStr = new String(original);
        } catch (Exception e) {
            throw ExceptionResultEnum.ERROR.exception("登录失败:" + e.getMessage());
        }
        return JSONObject.parseObject(jsonStr);
    }

    /**
     * 获取鉴权
     *
     * @param loginCode
     * @return
     */
    private WxappSession getAuthorization(String loginCode) {
        Optional.ofNullable(wxappId).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("未配置微信小程序appid"));
        return solarService.getWxappSessionByCode(wxappId, loginCode);
    }
}
