package com.zanxiang.game.module.sdk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zanxiang.game.module.base.ServerInfo;
import com.zanxiang.game.module.base.pojo.dto.H5GameConfigDTO;
import com.zanxiang.game.module.base.pojo.enums.HttpStatusEnum;
import com.zanxiang.game.module.base.rpc.IKfMsgRpc;
import com.zanxiang.game.module.mybatis.entity.Game;
import com.zanxiang.game.module.mybatis.entity.GameApplet;
import com.zanxiang.game.module.mybatis.entity.GameExt;
import com.zanxiang.game.module.mybatis.mapper.GameAppletMapper;
import com.zanxiang.game.module.sdk.pojo.dto.AppletMsgDTO;
import com.zanxiang.game.module.sdk.pojo.dto.GameAppletDTO;
import com.zanxiang.game.module.sdk.pojo.param.UserData;
import com.zanxiang.game.module.sdk.pojo.vo.GameInitVO;
import com.zanxiang.game.module.sdk.service.IGameAppletService;
import com.zanxiang.game.module.sdk.service.IGameExtService;
import com.zanxiang.game.module.sdk.service.IGameService;
import com.zanxiang.game.module.sdk.service.api.WxApiService;
import com.zanxiang.game.module.sdk.service.pay.MiPayService;
import com.zanxiang.game.module.sdk.util.SignUtil;
import com.zanxiang.module.util.JsonUtil;
import com.zanxiang.module.util.bean.BeanUtil;
import com.zanxiang.module.util.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import reactor.util.function.Tuples;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author : lingfeng
 * @time : 2022-07-08
 * @description : 微信小游戏
 */
@Slf4j
@Service
public class GameAppletServiceImpl extends ServiceImpl<GameAppletMapper, GameApplet> implements IGameAppletService {

    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private IKfMsgRpc kfMsgRpc;

    @Value("${payConfig.wxPay.customH5Url}")
    private String customH5Url;

    @Autowired
    private IGameService gameService;

    @Autowired
    private IGameExtService gameExtService;

    @Autowired
    private MiPayService miPayService;

    @Autowired
    private WxApiService wxApiService;

    @Autowired
    private IGameAppletService gameAppletService;

    @Override
    public Map<String, String> getAppletToken(String gameAppId) {
        GameExt gameExt = gameExtService.getByGameAppId(gameAppId);
        if (gameExt == null) {
            throw new BaseException("参数错误, 游戏配置信息不存在");
        }
        GameAppletDTO gameAppletDTO = gameAppletService.getByGameId(gameExt.getGameId());
        if (gameAppletDTO == null) {
            throw new BaseException("参数错误, 微信小游戏配置信息不存在");
        }
        String accessToken = wxApiService.getAccessToken(gameAppletDTO.getAppId(), gameAppletDTO.getAppSecret());
        Map<String, String> resultMap = new HashMap<>(2);
        resultMap.put("appId", gameAppletDTO.getAppId());
        resultMap.put("accessToken", accessToken);
        return resultMap;
    }

    @Override
    public String appletMsgCheck(String appId, String signature, String timestamp, String nonce, String echoStr) throws Exception {
        log.error("appId : {}, signature : {}, timestamp : {}, nonce : {}, echoStr : {}", appId, signature, timestamp, nonce, echoStr);
        GameAppletDTO gameAppletDTO = this.getByAppId(appId);
        GameAppletDTO.MsgConfigBean msgConfigBean = gameAppletDTO.getMsgConfigBean();
        if (msgConfigBean == null) {
            throw new BaseException("参数错误, 小游戏应用缺少消息推送配置");
        }
        String mySignature = SignUtil.SHA1(msgConfigBean.getMsgPushToken(), timestamp, nonce);
        if (!Objects.equals(mySignature, signature)) {
            throw new BaseException("验证签名不匹配");
        }
        return echoStr;
    }

    @Override
    public String appletMsg(String appId, String signature, String timestamp, String nonce, String postData) throws Exception {
        String result = HttpStatusEnum.SUCCESS.getMsg();
        if (Strings.isBlank(postData)) {
            return result;
        }
        GameAppletDTO gameAppletDTO = this.getByAppId(appId);
        GameAppletDTO.MsgConfigBean msgConfigBean = gameAppletDTO.getMsgConfigBean();
        if (msgConfigBean == null) {
            return result;
        }
        String mySignature = SignUtil.SHA1(msgConfigBean.getMsgPushToken(), timestamp, nonce);
        if (!Objects.equals(mySignature, signature)) {
            log.error("计算出来的签名不匹配, msgConfigBean : {}, signature : {},  mySignature : {}", JsonUtil.toString(msgConfigBean),
                    signature, mySignature);
            return result;
        }
        //消息内容
        AppletMsgDTO appletMsgDTO = JsonUtil.toObj(postData, AppletMsgDTO.class);
        //米大师支付回调事件
        if (Objects.equals(appletMsgDTO.getMsgType(), AppletMsgDTO.MSG_TYPE_EVENT)
                && Objects.equals(appletMsgDTO.getEvent(), AppletMsgDTO.EVENT_MI_PAY_CALL_BACK)) {
            return this.miPayMessage(appletMsgDTO, gameAppletDTO);
        }
        //消息转发到后台客服系统处理
        try {
            kfMsgRpc.appletMsg(postData);
        } catch (Exception ignored) {
        }
        //其他消息不处理, 直接返回成功
        return result;
    }

    private String miPayMessage(AppletMsgDTO appletMsgDTO, GameAppletDTO gameAppletDTO) {
        Map<String, Object> failResultMap = new HashMap<>(2);
        failResultMap.put("ErrCode", 99999L);
        failResultMap.put("ErrMsg", "internal error");
        String failResult = JsonUtil.toString(failResultMap);
        Map<String, Object> successResultMap = new HashMap<>(2);
        successResultMap.put("ErrCode", 0L);
        successResultMap.put("ErrMsg", "Success");
        String successResult = JsonUtil.toString(successResultMap);
        //通知内容
        AppletMsgDTO.MiniGameBean miniGame = appletMsgDTO.getMiniGame();
        //判断是否为模拟数据, 模拟数据直接返回, 不进行业务处理
        if (Objects.equals(miniGame.getIsMock(), Boolean.TRUE)) {
            log.error("模拟调试返回返回成功, 返回结果 successResult : {}", successResult);
            return successResult;
        }
        //支付内容
        String payload = miniGame.getPayload();
        AppletMsgDTO.PayloadBean payloadBean = JsonUtil.toObj(payload, AppletMsgDTO.PayloadBean.class);
        if (payloadBean == null) {
            log.error("米大师支付回调参数错误, 没有支付信息 appletMsgDTO : {}", JsonUtil.toString(appletMsgDTO));
            return failResult;
        }
        //商户订单号和交易订单号判断
        AppletMsgDTO.WeChatPayInfoBean weChatPayInfo = payloadBean.getWeChatPayInfo();
        if (weChatPayInfo == null || Strings.isBlank(weChatPayInfo.getMchOrderNo())
                || Strings.isBlank(weChatPayInfo.getTransactionId())) {
            log.error("米大师支付回调参数错误, 没有支付信息 appletMsgDTO : {}", JsonUtil.toString(appletMsgDTO));
            return failResult;
        }
        //查询游戏小程序信息
        String appKey = gameAppletDTO.getMiPayConfigBean().getAppKey();
        //计算签名
        String mySign = miPayService.calcSignature(appletMsgDTO.getEvent() + "&" + miniGame.getPayload(), appKey);
        //签名不匹配
        if (!Objects.equals(mySign, miniGame.getPayEventSig())) {
            log.error("米大师支付回调签名不匹配, mySign : {}, PayEventSig : {}, appKey : {}",
                    mySign, miniGame.getPayEventSig(), appKey);
            return failResult;
        }
        //更新订单信息
        Boolean miPayNotifyResult = miPayService.miPayNotify(payloadBean.getOutTradeNo(),
                Tuples.of(weChatPayInfo.getMchOrderNo(), weChatPayInfo.getTransactionId()));
        //放业务更新结果
        return Objects.equals(miPayNotifyResult, Boolean.TRUE) ? successResult : failResult;
    }

    @Override
    public GameAppletDTO getByGameId(Long gameId) {
        GameApplet gameApplet = super.getOne(new LambdaQueryWrapper<GameApplet>()
                .eq(GameApplet::getGameId, gameId));
        if (gameApplet == null) {
            throw new BaseException("参数错误, 游戏小程序信息不存在");
        }
        return BeanUtil.copy(gameApplet, GameAppletDTO.class);
    }

    @Override
    public GameAppletDTO getByAppId(String appId) {
        GameApplet gameApplet = super.getOne(new LambdaQueryWrapper<GameApplet>()
                .eq(GameApplet::getAppId, appId));
        if (gameApplet == null) {
            log.error("参数错误, 游戏小程序信息不存在, appId : {}", appId);
            throw new BaseException("参数错误, 游戏小程序信息不存在");
        }
        return BeanUtil.copy(gameApplet, GameAppletDTO.class);
    }

    @Override
    public GameInitVO h5AndAppletInit(UserData userData) {
        //游戏
        Game game = gameService.getById(userData.getGameId());
        if (game == null) {
            throw new BaseException("参数错误, 游戏信息不存在");
        }
        //H5游戏配置
        String h5GameConfig = game.getH5GameConfig();
        //游戏应用信息
        GameApplet gameApplet = super.getOne(new LambdaQueryWrapper<GameApplet>()
                .eq(GameApplet::getGameId, userData.getGameId()));
        GameExt gameExt = gameExtService.getByGameId(userData.getGameId());
        //构造初始化信息
        return GameInitVO.builder()
                .appId(gameApplet == null ? null : gameApplet.getAppId())
                .appKey(gameExt == null ? null : gameExt.getAppKey())
                .gameName(game.getName())
                .isPut(game.getIsPut())
                .h5GameConfigDTO(Strings.isBlank(h5GameConfig) ? null : JsonUtil.toObj(h5GameConfig, H5GameConfigDTO.class))
                .h5GameConfigMap(Strings.isBlank(h5GameConfig) ? null : JsonUtil.toMap(h5GameConfig, Map.class, Object.class))
                .build();
    }
}
