package com.liquidnet.service.sweet.service.impl;

import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.DESUtils;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.adam.dto.vo.AdamLoginInfoVo;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.feign.adam.api.FeignAdamBaseClient;
import com.liquidnet.service.sweet.dto.vo.WechatAppletLoginInfoVo;
import com.liquidnet.service.sweet.dto.vo.WechatTokenInfoVo;
import com.liquidnet.service.sweet.dto.vo.WechatUserInfoVo;
import com.liquidnet.service.sweet.utils.QueueUtils;
import com.liquidnet.service.sweet.utils.WechatUsersRedisUtils;
import com.liquidnet.service.sweet.vo.SweetAppletUsersVo;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.LinkedList;
import java.util.Objects;

/**
 * <p>
 * 小程序登陆 服务实现类
 * </p>
 *
 * @author liquidnet
 * @since 2021-07-27
 */
@Slf4j
@Service
public class SweetWechatLoginServiceImpl {

    @Autowired
    private SweetWechatService sweetWechatService;

    @Autowired
    QueueUtils queueUtils;

    @Autowired
    private WechatUsersRedisUtils redisUtils;

    @Autowired
    @Lazy
    private FeignAdamBaseClient feignAdamBaseClient;

    public ResponseDto<WechatAppletLoginInfoVo> userInfo(String userCode, String code, String encryptedData, String iv, Integer type) {
        log.info("小程序解密用户信息参数：[code=[{}], encryptedData=[{}], [iv=[{}], type=[{}]", code, encryptedData, iv, type);
        try {
            WxMaJscode2SessionResult sessionInfo = null;
            WxMaPhoneNumberInfo wxMaPhoneNumberInfo = null;
            String unionId = null;
            String openId = null;
            if (type == 1 && Objects.isNull(encryptedData)) {
                // 获取unid openid
                sessionInfo = sweetWechatService.sessionInfo(userCode, type);
                // 解密手机号码信息
                wxMaPhoneNumberInfo = sweetWechatService.getPhoneNumber(code, type);
            } else {
                sessionInfo = sweetWechatService.sessionInfo(code, type);
                wxMaPhoneNumberInfo = sweetWechatService.phoneNumberInfo(sessionInfo.getSessionKey(), encryptedData, iv, type);
            }
            if (Objects.isNull(sessionInfo)) {
                return ResponseDto.failure("login handler error");
            }
            unionId = sessionInfo.getUnionid();
            openId = sessionInfo.getOpenid();
            if (Objects.isNull(wxMaPhoneNumberInfo) || StringUtils.isBlank(wxMaPhoneNumberInfo.getPhoneNumber())) {
                return ResponseDto.failure("获取手机信息失败");
            }
            String phoneNumber = wxMaPhoneNumberInfo.getPhoneNumber();
            String purePhoneNumber = wxMaPhoneNumberInfo.getPurePhoneNumber();
            String countryCode = wxMaPhoneNumberInfo.getCountryCode();
            boolean needSweetBinding = null == redisUtils.getSweetAppletUsersOfPhone(purePhoneNumber);
            ResponseDto<AdamLoginInfoVo> loginResp = this.loginByPhoneWithWechat(
                    purePhoneNumber, openId, unionId, needSweetBinding ? 1 : null);
            if (!loginResp.isSuccess() || null == loginResp.getData()) {
                return ResponseDto.failure(loginResp.getCode(), loginResp.getMessage());
            }
            if (needSweetBinding) {
                this.bindSweetAppletUser(
                        loginResp.getData().getUserInfo().getUid(),
                        openId, unionId, phoneNumber, purePhoneNumber, countryCode, type);
            }
            return ResponseDto.success(WechatAppletLoginInfoVo.wrap(
                    loginResp.getData(), phoneNumber, purePhoneNumber, countryCode, openId, unionId));
        } catch (Exception e) {
            log.error("WechatUserInfoError Exception:{}", e);
            return ResponseDto.failure();
        }
    }

    public ResponseDto<String> wxaCode2Session(String jsCode, Integer type) {
        try {
            WxMaJscode2SessionResult wxMaJscode2SessionResult = sweetWechatService.sessionInfo(jsCode, type);
            return ResponseDto.success(wxMaJscode2SessionResult.getOpenid());
        } catch (Exception e) {
            log.error("wxaCode2SessionError Exception:{}", e);
            return ResponseDto.failure();
        }
    }

    public ResponseDto<String> wxOauth2AccessToken(String code, Integer type) {
        try {
            WxOAuth2AccessToken wxOAuth2AccessToken = sweetWechatService.wxOauth2AccessToken(code, type);
            return ResponseDto.success(wxOAuth2AccessToken.getOpenId());
        } catch (Exception e) {
            log.error("wxaCode2SessionError Exception:{}", e);
            return ResponseDto.failure();
        }
    }

    public ResponseDto<WechatTokenInfoVo> mpWxOauth2AccessTokenInfo(String code, Integer type) {
        try {
            WxOAuth2AccessToken wxOAuth2AccessToken = sweetWechatService.wxOauth2AccessToken(code, type);
            WechatTokenInfoVo userInfoVo = WechatTokenInfoVo.getNew().copy(wxOAuth2AccessToken);
            return ResponseDto.success(userInfoVo);
        } catch (Exception e) {
            log.error("wxaCode2SessionError Exception:{}", e);
            return ResponseDto.failure();
        }
    }

    public ResponseDto<WechatUserInfoVo> mpUserInfo(String code, Integer type) {
        try {
            WxOAuth2UserInfo wxOAuth2UserInfo = sweetWechatService.mpUserInfo(code, type);
            WechatUserInfoVo tokenInfoVo = WechatUserInfoVo.getNew().copy(wxOAuth2UserInfo);
            return ResponseDto.success(tokenInfoVo);
        } catch (Exception e) {
            log.error("wxaCode2SessionError Exception:{}", e);
            return ResponseDto.failure();
        }
    }

    public ResponseDto<AdamLoginInfoVo> userInfoMa(String jsCode, Integer type) {
        try {
            WxMaJscode2SessionResult sessionResult = sweetWechatService.sessionInfo(jsCode, type);
            SweetAppletUsersVo sweetAppletUsersVo = redisUtils.getSweetAppletUsersOfUnionId(sessionResult.getUnionid());
            if (null == sweetAppletUsersVo) {
                return ResponseDto.failure(ErrorMapping.get("90081"));
            }
            return this.loginByPhoneWithWechat(
                    sweetAppletUsersVo.getPurePhoneNumber(),
                    sweetAppletUsersVo.getOpenId(),
                    sweetAppletUsersVo.getUnionId()
            );
        } catch (Exception e) {
            log.error("userInfoMaError Exception:{}", e);
            return ResponseDto.failure("获取用户信息失败～");
        }
    }

    private ResponseDto<AdamLoginInfoVo> loginByPhoneWithWechat(String purePhone, String openId, String unionId) {
        return this.loginByPhoneWithWechat(purePhone, openId, unionId, null);
    }

    private ResponseDto<AdamLoginInfoVo> loginByPhoneWithWechat(String purePhone, String openId, String unionId, Integer isComplete) {
        try {
            String otp = DESUtils.DES().encrypt(purePhone + LocalDateTime.now().toEpochSecond(ZoneOffset.UTC));
            ResponseDto<AdamLoginInfoVo> loginResp = feignAdamBaseClient.loginBySilentMobile(purePhone, otp, isComplete);
            if (!loginResp.isSuccess() || null == loginResp.getData()) {
                return ResponseDto.failure(loginResp.getCode(), loginResp.getMessage());
            }
            AdamLoginInfoVo loginInfoVo = loginResp.getData();
            loginInfoVo.setWechatOpenid(openId);
            loginInfoVo.setWechatUnionid(unionId);
            return ResponseDto.success(loginInfoVo);
        } catch (Exception e) {
            log.error("loginByPhoneWithWechat error, mobile:{}", purePhone, e);
            return ResponseDto.failure("获取用户信息失败～");
        }
    }

    private void bindSweetAppletUser(String userId, String openId, String unionId, String phoneNumber,
                                     String purePhoneNumber, String countryCode, Integer type) {
        String appletUserId = IDGenerator.nextSnowId();
        LinkedList<String> sqlsA = CollectionUtil.linkedListString();
        LinkedList<Object[]> sqlsDataA = CollectionUtil.linkedListObjectArr();
        sqlsA.add(SqlMapping.get("sweet_applet_users.insert"));
        sqlsDataA.add(new Object[]{
                appletUserId, userId, openId, unionId, phoneNumber, purePhoneNumber, countryCode, type
        });
        queueUtils.sendMsgByRedis(MQConst.SweetQueue.SWEET_WECHAT_USER_INSERT.getKey(),
                SqlMapping.gets(sqlsA, sqlsDataA));
        SweetAppletUsersVo sweetAppletUsersVo = SweetAppletUsersVo.getNew();
        sweetAppletUsersVo.setAppletUserId(appletUserId);
        sweetAppletUsersVo.setUserId(userId);
        sweetAppletUsersVo.setOpenId(openId);
        sweetAppletUsersVo.setUnionId(unionId);
        sweetAppletUsersVo.setPhoneNumber(phoneNumber);
        sweetAppletUsersVo.setPurePhoneNumber(purePhoneNumber);
        sweetAppletUsersVo.setCountryCode(countryCode);
        sweetAppletUsersVo.setType(type);
        redisUtils.setSweetAppletUsers(sweetAppletUsersVo);
    }
}
