package com.cool.store.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.cool.store.constants.CommonConstants;
import com.cool.store.dao.*;
import com.cool.store.dto.wx.*;
import com.cool.store.entity.*;
import com.cool.store.enums.*;
import com.cool.store.exception.ServiceException;
import com.cool.store.http.WechatRest;
import com.cool.store.mapper.HyPartnerUserChannelMapper;
import com.cool.store.request.MobileUpdateRequest;
import com.cool.store.service.WechatMiniAppService;
import com.cool.store.utils.RedisUtilPool;
import com.cool.store.utils.UUIDUtils;
import com.cool.store.vo.PartnerUserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Objects;

/**
 * @author zhangchenbiao
 * @FileName: WechatMiniAppServiceImpl
 * @Description:
 * @date 2023-05-29 14:29
 */
@Slf4j
@Service
public class WechatMiniAppServiceImpl implements WechatMiniAppService {

    @Resource
    private RedisUtilPool redisUtilPool;
    @Resource
    private WechatRest wechatRest;
    @Resource
    private HyPartnerUserInfoDAO hyPartnerUserInfoDAO;
    @Autowired
    private HyPartnerUserChannelMapper hyPartnerUserChannelMapper;
    @Resource
    private LineInfoDAO lineInfoDAO;
    @Resource
    HyOpenAreaInfoDAO hyOpenAreaInfoDAO;
    @Resource
    PartnerUserWechatBindDAO partnerUserWechatBindDAO;
    @Value("${weixin.appId}")
    private String wxAppId;
    @Value("${weixin.appSecret}")
    private String wxAppSecret;
    @Value("${fixMobileOpenid}")
    private String fixMobileOpenid;
    @Value("${exhibition.channel.id}")
    private Integer exhibition;
    @Value("${recommended.channel.id}")
    private Integer recommended;

    @Override
    public PartnerUserInfoVO miniProgramLogin(MiniProgramLoginDTO param) {
        log.info("miniProgramLogin #param {}", JSONObject.toJSONString(param));
        PartnerUserInfoVO userInfoVO = new PartnerUserInfoVO();
        String jsCode = param.getJsCode();
        String lockKey = "codeSession:" + wxAppId + CommonConstants.MOSAICS + jsCode;
        boolean lock = redisUtilPool.lock(lockKey);
        if (!lock) {
            throw new ServiceException(ErrorCodeEnum.OPERATION_OVER_TIME);
        }
        CodeSessionDTO codeSession = wechatRest.miniProgramJsCodeSession(wxAppId, wxAppSecret, jsCode);
        String openid = codeSession.getOpenid();
        String unionId = codeSession.getUnionId();
        log.info("小程序登录:{}", unionId);
        log.info("sessionKey {}", codeSession.getSessionKey());
        // 获取小程序token
        String accessToken = wechatRest.getAccessToken(wxAppId, wxAppSecret);
        // 获取手机号码
        PhoneInfoDTO phoneInfoDTO = wechatRest.getUserPhoneNumber(param.getMobileCode(), accessToken);
        if (Objects.isNull(phoneInfoDTO) || Objects.isNull(phoneInfoDTO.getPhoneInfo())){
            throw new ServiceException(ErrorCodeEnum.LOGIN_ERROR_MOBILE_ERROR);
        }
        //通过手机号查询用户信
        HyPartnerUserInfoDO  hyPartnerUserInfoDO = hyPartnerUserInfoDAO.selectByMobile(phoneInfoDTO.getPhoneInfo().getPhoneNumber());
        if (hyPartnerUserInfoDO == null) {
            //系统中没有改手机号 需要先注册该手机号
            hyPartnerUserInfoDO = new  HyPartnerUserInfoDO();
            hyPartnerUserInfoDO.setMobile(phoneInfoDTO.getPhoneInfo().getPhoneNumber());
            hyPartnerUserInfoDO.setOpenid(openid);
            hyPartnerUserInfoDO.setPartnerId(UUIDUtils.get32UUID());
            hyPartnerUserInfoDAO.insertSelective(hyPartnerUserInfoDO);
            LineInfoDO lineInfoDO = new LineInfoDO();
            lineInfoDO.setLineStatus(LineStatusEnum.PUBLIC_SEAS.getCode());
            lineInfoDO.setWorkflowSubStage(WorkflowSubStageEnum.INTEND.getCode());
            lineInfoDO.setMobile(hyPartnerUserInfoDO.getMobile());
            lineInfoDO.setWorkflowStage(WorkflowStageEnum.INTENT.getCode());
            lineInfoDO.setFranchiseBrand(String.valueOf(FranchiseBrandEnum.ZXJP.getCode()));
            lineInfoDO.setWorkflowSubStageStatus(WorkflowSubStageStatusEnum.INTENT_0.getCode());
            lineInfoDO.setPartnerId(hyPartnerUserInfoDO.getPartnerId());
            lineInfoDAO.insertOrUpdate(lineInfoDO);
        }
        //判断绑定表种是否绑定了手机号与微信openid
        PartnerUserWechatBindDO zlPartnerUserBindDO = partnerUserWechatBindDAO.getByOpenIdAndPartnerId(hyPartnerUserInfoDO.getPartnerId(), openid);
        if (zlPartnerUserBindDO == null) {
            //绑定手机号与微信openid
            PartnerUserWechatBindDO bindDO = new PartnerUserWechatBindDO();
            bindDO.setBindTime(new Date());
            bindDO.setOpenId(openid);
            bindDO.setUnionId(unionId);
            bindDO.setPartnerId(hyPartnerUserInfoDO.getPartnerId());
            bindDO.setCreateTime(new Date());
            partnerUserWechatBindDAO.insertSelective(bindDO);
        }else {
            //维护unionId 针对老数据没有unionId
            if (zlPartnerUserBindDO.getUnionId()==null){
                zlPartnerUserBindDO.setUnionId(unionId);
                partnerUserWechatBindDAO.update(zlPartnerUserBindDO);
            }
        }
        BeanUtil.copyProperties(hyPartnerUserInfoDO, userInfoVO);
        fillLineInfo(userInfoVO, hyPartnerUserInfoDO.getPartnerId());
        //将数据存储到redis中
        String key = MessageFormat.format(CommonConstants.ZXJP_MINI_PROGRAM_LOGIN_FLAG, openid);
        redisUtilPool.setString(key,JSONObject.toJSONString(userInfoVO),CommonConstants.SEVEN_DAY_SECONDS);
        return userInfoVO;
    }

    @Override
    public PartnerUserInfoVO getUserInfoByJsCode(String jsCode) {
        PartnerUserInfoVO userInfoVO = null;
        String lockKey = "codeSession:" + wxAppId + CommonConstants.MOSAICS + jsCode;
        boolean lock = redisUtilPool.lock(lockKey);
        if (!lock) {
            throw new ServiceException(ErrorCodeEnum.OPERATION_OVER_TIME);
        }
        CodeSessionDTO codeSession = wechatRest.miniProgramJsCodeSession(wxAppId, wxAppSecret, jsCode);
        String openid = codeSession.getOpenid();
        String key = MessageFormat.format(CommonConstants.ZXJP_MINI_PROGRAM_LOGIN_FLAG, openid);
        String value = redisUtilPool.getString(key);
        if (StringUtils.isNotEmpty(value)){
            //设置有效期
            Long expire = redisUtilPool.getExpire(key);
            if (expire < CommonConstants.THREE_DAY_SECONDS){
                //重置时间为7天
                redisUtilPool.expire(key,CommonConstants.SEVEN_DAY_SECONDS);
            }
            userInfoVO = JSONObject.parseObject(value,PartnerUserInfoVO.class);
        }
        return userInfoVO;
    }

    @Override
    public Boolean cancellation(String openId) {
        //删除登录态缓存
        String key = MessageFormat.format(CommonConstants.ZXJP_MINI_PROGRAM_LOGIN_FLAG, openId);
        redisUtilPool.delKey(key);
        return Boolean.TRUE;
    }


    @Override
    public String getUserPhoneNumber(String mobileCode) {
        // 获取小程序token
        String accessToken = wechatRest.getAccessToken(wxAppId, wxAppSecret);
        // 获取手机号码
        PhoneInfoDTO phoneInfoDTO = wechatRest.getUserPhoneNumber(mobileCode, accessToken);
        if(phoneInfoDTO != null && phoneInfoDTO.getPhoneInfo() != null && StringUtils.isNotBlank(phoneInfoDTO.getPhoneInfo().getPhoneNumber())){
            return phoneInfoDTO.getPhoneInfo().getPhoneNumber();
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor =  Exception.class)
    public String updateUserPhoneNumber(MobileUpdateRequest request, PartnerUserInfoVO userInfoVO) {
        String newMobile = "";
        HyPartnerUserInfoDO oldUserInfo = hyPartnerUserInfoDAO.selectByMobile(userInfoVO.getMobile());
        if (oldUserInfo == null) {
            throw new ServiceException(ErrorCodeEnum.PARTNER_USER_NOT_EXIST);
        }
        // 获取小程序token
        String accessToken = wechatRest.getAccessToken(wxAppId, wxAppSecret);
        // 获取手机号码
        PhoneInfoDTO phoneInfoDTO = wechatRest.getUserPhoneNumber(request.getMobileCode(), accessToken);
        if(phoneInfoDTO != null && phoneInfoDTO.getPhoneInfo() != null && StringUtils.isNotBlank(phoneInfoDTO.getPhoneInfo().getPhoneNumber())){
            newMobile = phoneInfoDTO.getPhoneInfo().getPhoneNumber();
            if(newMobile.equals(oldUserInfo.getMobile())){
                return newMobile;
            }
            HyPartnerUserInfoDO newUserInfo = hyPartnerUserInfoDAO.selectByMobile(newMobile);
            Boolean lineInfoByMobile = lineInfoDAO.getLineInfoByMobile(newMobile);
            if (newUserInfo != null&& lineInfoByMobile) {
                throw new ServiceException(ErrorCodeEnum.NEW_MOBILE_HAS_EXIST);
            }
            LineInfoDO lineInfoDO = lineInfoDAO.getByPartnerId(userInfoVO.getPartnerId());
            lineInfoDO.setMobile(newMobile);
            lineInfoDO.setUpdateTime(new Date());
            oldUserInfo.setMobile(newMobile);
            hyPartnerUserInfoDAO.updateByPrimaryKeySelective(oldUserInfo);
            lineInfoDAO.updateLineInfo(lineInfoDO);
            //修改意向申请信息中的手机号
            // hyPartnerBaseInfoDAO.updateByPartnerId(null, newMobile, oldUserInfo.getPartnerId());
        }
        return newMobile;
    }

    @Override
    public PartnerUserInfoVO getUserInfo(String mobile, String openId) {
        PartnerUserInfoVO userInfoVO = new PartnerUserInfoVO();
        if(fixMobileOpenid.equals(mobile)){
            userInfoVO.setMobile(mobile);
            userInfoVO.setOpenid(mobile);
            userInfoVO.setPartnerId("");
            return  userInfoVO;
        }
        HyPartnerUserInfoDO hyPartnerUserInfoDO = hyPartnerUserInfoDAO.selectByMobile(mobile);
        if(hyPartnerUserInfoDO != null){
            BeanUtil.copyProperties(hyPartnerUserInfoDO, userInfoVO);
            /*if(StringUtils.isNotBlank(hyPartnerUserInfoDO.getWantShopArea())){
                HyOpenAreaInfoDO hyOpenAreaInfoDO = hyOpenAreaInfoDAO.selectById(Long.valueOf(hyPartnerUserInfoDO.getWantShopArea()));
                userInfoVO.setWantShopAreaName(hyOpenAreaInfoDO.getAreaPath().replace("/", " ").trim());
            }*/
            fillLineInfo(userInfoVO, hyPartnerUserInfoDO.getPartnerId());
        }
        return userInfoVO;
    }

    private void fillLineInfo(PartnerUserInfoVO userInfoVO, String partnerId) {
        LineInfoDO lineInfoDO = lineInfoDAO.getByPartnerId(partnerId);
        if (lineInfoDO != null){
            userInfoVO.setLiveAddress(lineInfoDO.getLiveAddress());
            userInfoVO.setUsername(lineInfoDO.getUsername());
            userInfoVO.setLineId(lineInfoDO.getId());
            userInfoVO.setWorkflowStage(lineInfoDO.getWorkflowStage());
            userInfoVO.setWorkflowSubStage(lineInfoDO.getWorkflowSubStage());
            userInfoVO.setWorkflowSubStageStatus(lineInfoDO.getWorkflowSubStageStatus());
            userInfoVO.setLineStatus(lineInfoDO.getLineStatus());
            userInfoVO.setLineSource(lineInfoDO.getLineSource());
        }
    }

    @Override
    public String getMiniAppUrl() {
        MiniAppUrlLinkReqDTO miniAppUrlLinkReqDTO = new MiniAppUrlLinkReqDTO();
        return getMiniAppUrlLink(miniAppUrlLinkReqDTO);
    }

    @Override
    public String getMiniAppUrlLink(MiniAppUrlLinkReqDTO miniAppUrlLinkReqDTO) {
        String accessToken = wechatRest.getAccessToken(wxAppId, wxAppSecret);
        MiniAppUrlLinkDTO miniAppUrlLink = wechatRest.getMiniAppUrlLink(accessToken, miniAppUrlLinkReqDTO);
        if (miniAppUrlLink != null){
            return miniAppUrlLink.getUrlLink();
        }
        return null;
    }

    @Override
    public String getShortTermTokenByMobile(MiniProgramFreeLoginDTO param) {
        HyPartnerUserInfoDO hyPartnerUserInfoDO = hyPartnerUserInfoDAO.selectByMobile(param.getMobile());
        if (Objects.isNull(hyPartnerUserInfoDO)) {
            return "";
        }
        PartnerUserInfoVO userInfoVO = BeanUtil.toBean(hyPartnerUserInfoDO, PartnerUserInfoVO.class);
        fillLineInfo(userInfoVO, hyPartnerUserInfoDO.getPartnerId());
        String token = new SecureRandomNumberGenerator().nextBytes().toHex();
        String key = MessageFormat.format(CommonConstants.ZXJP_MIN_PROGRAM_SHORT_TERM_LOGIN_FLAG, token);
        redisUtilPool.setString(key, JSONObject.toJSONString(userInfoVO), CommonConstants.SHORT_TERM_TOKEN_EXPIRE);
        return token;
    }

    @Override
    public PartnerUserInfoVO getUserInfoByShortTermToken(String token) {
        String key = MessageFormat.format(CommonConstants.ZXJP_MIN_PROGRAM_SHORT_TERM_LOGIN_FLAG, token);
        String userStr = redisUtilPool.getString(key);
        if (StringUtils.isNotBlank(userStr)) {
            PartnerUserInfoVO userInfoVO = JSONObject.parseObject(userStr, PartnerUserInfoVO.class);
            redisUtilPool.delKey(key);
            return userInfoVO;
        }
        return null;
    }

}
