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

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.sd4324530.jtuple.Tuple2;
import com.github.sd4324530.jtuple.Tuple3;
import com.zanxiang.game.back.base.ServerInfo;
import com.zanxiang.game.back.base.pojo.dto.TencentAppOrderBackQueryRpcDTO;
import com.zanxiang.game.back.base.pojo.dto.TtAppOrderBackQueryRpcDTO;
import com.zanxiang.game.back.base.pojo.enums.OrderStatusEnum;
import com.zanxiang.game.back.base.pojo.vo.GameBackPolicyRpcVO;
import com.zanxiang.game.back.base.pojo.vo.OrderBackQueryRpcVO;
import com.zanxiang.game.back.base.rpc.IGameBackPolicyRpc;
import com.zanxiang.game.back.base.rpc.ITencentAppBackRpc;
import com.zanxiang.game.back.base.rpc.ITencentMiniGameBackRpc;
import com.zanxiang.game.back.base.rpc.ITtAppBackRpc;
import com.zanxiang.game.module.base.pojo.enums.AccountTypeEnum;
import com.zanxiang.game.module.base.pojo.enums.GameCategoryEnum;
import com.zanxiang.game.module.mybatis.entity.*;
import com.zanxiang.game.module.mybatis.mapper.GameBackLogMediaSdkMapper;
import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
import com.zanxiang.game.module.sdk.enums.CallBackTypeEnum;
import com.zanxiang.game.module.sdk.pojo.dto.PlatformOrderDTO;
import com.zanxiang.game.module.sdk.pojo.param.BackLogMediaSdkParam;
import com.zanxiang.game.module.sdk.pojo.param.CallBackControlParam;
import com.zanxiang.game.module.sdk.pojo.param.UserData;
import com.zanxiang.game.module.sdk.service.*;
import com.zanxiang.module.redis.service.IDistributedLockComponent;
import com.zanxiang.module.util.JsonUtil;
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.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author : lingfeng
 * @time : 2022-11-10
 * @description : 媒体sdk回传日志
 */
@Slf4j
@Service
public class GameBackLogMediaSdkServiceImpl extends ServiceImpl<GameBackLogMediaSdkMapper, GameBackLogMediaSdk> implements IGameBackLogMediaSdkService {

    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private ITtAppBackRpc ttAppBackRpc;

    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private IGameBackPolicyRpc gameBackPolicyRpc;

    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private ITencentMiniGameBackRpc tencentMiniGameBackRpc;

    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private ITencentAppBackRpc tencentAppBackRpc;

    @Autowired
    private IUserService userService;

    @Autowired
    private IOrderService orderService;

    @Autowired
    private IAgentService agentService;

    @Autowired
    private IGameService gameService;

    @Autowired
    private IUserEventService userEventService;

    @Autowired
    private IGameUserRoleService gameUserRoleService;

    @Autowired
    private IUserLoginLogService userLoginLogService;

    @Autowired
    private IDistributedLockComponent distributedLockComponent;

    @Override
    public Map<String, Object> callBackJudge(CallBackControlParam param, UserData userData) {
        //线程锁, 避免重复请求
        String lockKey = this.getLockKey(param, userData.getUserId());
        if (!distributedLockComponent.doLock(lockKey, 0L, 20L, TimeUnit.SECONDS)) {
            log.error("重复请求触发线程锁, 直接返回false, lockKey : {}", lockKey);
            Map<String, Object> resultMap = this.getResultMap(param, userData);
            resultMap.put("backMsg", "重复请求触发线程锁, 直接返回false, lockKey : " + lockKey);
            return resultMap;
        }
        //判断结果返回对象
        Map<String, Object> resultMap = this.getResultMap(param, userData);
        try {
            //参数判断, 检测不通过
            if (!this.callBackParamCheck(param, resultMap)) {
                return resultMap;
            }
            //查询玩家信息
            User user = userService.getById(userData.getUserId());
            //测试账号判断
            if (this.userEventTest(param, user, resultMap)) {
                return resultMap;
            }
            //玩家渠道信息回传检测
            Tuple2<Boolean, Agent> userAgentCheckTuple2 = this.userAgentCheck(user, resultMap, param);
            if (!userAgentCheckTuple2.first) {
                return resultMap;
            }
            //执行回传条件判断
            this.checkCallBack(user, userAgentCheckTuple2.second, resultMap, param);
        } catch (Exception e) {
            log.error("事件回传判断, 出现异常, param : {},  userData : {}, e : {}", JsonUtil.toString(param),
                    JsonUtil.toString(userData), e.getMessage(), e);
            resultMap.put("backMsg", "事件回传判断, 出现异常");
        } finally {
            //等级提升和订单付费, 不释放锁, 其他事件锁释放
            if (!Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_UPDATE_LEVEL)
                    && !Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_PAY_ORDER)) {
                distributedLockComponent.unlock(lockKey);
            }
        }
        //返回结果
        return resultMap;
    }

    private Map<String, Object> getResultMap(CallBackControlParam param, UserData userData) {
        Map<String, Object> resultMap = new HashMap<>(10);
        resultMap.put("callBackTypeEnum", param.getCallBackTypeEnum().getValue());
        resultMap.put("userId", userData.getUserId());
        resultMap.put("gameId", userData.getGameId());
        resultMap.put("callBack", Boolean.FALSE);
        return resultMap;
    }

    private String getLockKey(CallBackControlParam param, Long userId) {
        //回传枚举
        CallBackTypeEnum callBackTypeEnum = param.getCallBackTypeEnum();
        //线程锁
        String lockKey = RedisKeyConstant.CALL_BACK_JUDGE_LOCK + callBackTypeEnum.getValue() + "_" + userId;
        //创角, 新手引导回传锁
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_ACTIVATE)
                || Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_CREATE_ROLE)
                || Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_TUTORIAL_FINISH)) {
            if (Strings.isNotBlank(param.getRoleId())) {
                lockKey = lockKey + "_" + param.getRoleId();
            }
        }
        //等级提升回传锁
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_UPDATE_LEVEL) && param.getLevel() != null) {
            lockKey = lockKey + "_" + param.getRoleId() + "_" + param.getLevel();
        }
        //付费回传锁
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_PAY_ORDER) && Strings.isNotBlank(param.getOrderId())) {
            lockKey = lockKey + "_" + param.getOrderId();
        }
        return lockKey;
    }

    private boolean callBackParamCheck(CallBackControlParam param, Map<String, Object> resultMap) {
        CallBackTypeEnum callBackTypeEnum = param.getCallBackTypeEnum();
        //创角, 新手引导, 必须传角色id
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_ACTIVATE)
                || Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_CREATE_ROLE)
                || Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_TUTORIAL_FINISH)) {
            if (Strings.isBlank(param.getRoleId())) {
                resultMap.put("backMsg", "参数错误, 创角, 新手引导或者等级提升回传, 必须传角色id");
                return false;
            }
            resultMap.put("roleId", param.getRoleId());
        }
        //等级提升回传, 必须传角色id, 角色当前等级
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_UPDATE_LEVEL)) {
            if (Strings.isBlank(param.getRoleId()) || param.getLevel() == null) {
                resultMap.put("backMsg", "参数错误,等级提升回传, 必须传角色id和角色当前等级");
                return false;
            }
            resultMap.put("roleId", param.getRoleId());
            resultMap.put("level", param.getLevel());
        }
        //付费回传, 必须传订单id
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_PAY_ORDER)) {
            if (Strings.isBlank(param.getOrderId())) {
                resultMap.put("backMsg", "参数错误, 付费回传, 必须传订单id");
                return false;
            }
            resultMap.put("orderId", param.getOrderId());
        }
        return true;
    }

    private boolean userEventTest(CallBackControlParam param, User user, Map<String, Object> resultMap) {
        //查询游戏信息
        Game game = gameService.getById(user.getGameId());
        //判断是否测试过事件用户
        if (!Objects.equals(game.getStatus(), 2) && userEventService.count(new LambdaQueryWrapper<UserEvent>()
                .eq(UserEvent::getGameId, user.getGameId())
                .and(qw -> qw.eq(UserEvent::getUsername, user.getUsername())
                        .or().eq(UserEvent::getUsername, user.getOpenId())
                        .or().eq(UserEvent::getMobile, user.getMobile()))
        ) <= 0) {
            return false;
        }
        //判断沉默唤起回传, 新手引导回传, 是否已经执行, 不重复执行
        if (Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_RE_ACTIVE)
                || Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_TUTORIAL_FINISH)) {
            if (super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                    .eq(GameBackLogMediaSdk::getGameId, user.getGameId())
                    .eq(GameBackLogMediaSdk::getUserId, user.getId())
                    .eq(GameBackLogMediaSdk::getCallBackTypeEnum, param.getCallBackTypeEnum().getValue())
            ) > 0) {
                return false;
            }
        }
        //等级提升回传, 判断当前等级是否已执行回传
        if (Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_UPDATE_LEVEL)) {
            if (super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                    .eq(GameBackLogMediaSdk::getGameId, user.getGameId())
                    .eq(GameBackLogMediaSdk::getUserId, user.getId())
                    .eq(GameBackLogMediaSdk::getRoleId, param.getRoleId())
                    .eq(GameBackLogMediaSdk::getLevel, param.getLevel())
                    .eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_UPDATE_LEVEL.getValue())
            ) > 0) {
                return false;
            }
        }
        //测试账号, 回传默认全量回传, 不做判断
        resultMap.put("callBack", Boolean.TRUE);
        resultMap.put("backMsg", "测试账号, 回传默认全量回传, 不做判断");
        //回传类型
        CallBackTypeEnum callBackTypeEnum = param.getCallBackTypeEnum();
        //判断是否沉默唤起回传, 添加沉默唤起判断天数
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_RE_ACTIVE)) {
            resultMap.put("backFlowDay", 30);
        }
        //判断是否订单付费回传, 添加回传金额
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_PAY_ORDER) && Strings.isNotBlank(param.getOrderId())) {
            PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(param.getOrderId());
            //APP直投回传
            if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_APP.getId())) {
                //头条APP媒体回传, 返回的单位是元
                if (Objects.equals(game.getPutType(), 1)) {
                    resultMap.put("amount", Collections.singletonList(platformOrderDTO.getAmount().intValue()));
                }
                //腾讯APP媒体回传, 返回的单位是分
                if (Objects.equals(game.getPutType(), 2)) {
                    int amount = platformOrderDTO.getAmount().multiply(new BigDecimal(100)).intValue();
                    resultMap.put("amount", Collections.singletonList(amount));
                }
            }
            //腾讯小游戏媒体, 腾讯广点通SDK回传, 返回的单位是分, 乘以100
            if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_WX_APPLET.getId())) {
                if (this.orderPayAndBackCheck(platformOrderDTO, param.getMipeiSuces())) {
                    resultMap.put("callBack", Boolean.FALSE);
                    resultMap.put("backMsg", "测试账号, 订单未支付或者已回传");
                } else {
                    int amount = platformOrderDTO.getAmount().multiply(new BigDecimal(100)).intValue();
                    resultMap.put("amount", Collections.singletonList(amount));
                }
            }
        }
        //返回结果
        return true;
    }

    private Tuple2<Boolean, Agent> userAgentCheck(User user, Map<String, Object> resultMap, CallBackControlParam param) {
        //玩家渠道
        Agent agent = agentService.getById(user.getAgentId());
        //买量用户, 返回执行回传, 且返回渠道信息
        if (agent != null && user.getShareUserId() == null) {
            return Tuple2.with(Boolean.TRUE, agent);
        }
        //查询游戏
        Game game = gameService.getById(user.getGameId());
        //自然量安卓APP, 不回传
        if (Objects.equals(game.getCategory(), GameCategoryEnum.CATEGORY_APP.getId())) {
            resultMap.put("backMsg", "自然量安卓APP, 不执行回传");
            return Tuple2.with(Boolean.FALSE, null);
        }
        //自然量微信小游戏, 新手引导无法回传, 无法定义新手引导回传等级
        if (Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_TUTORIAL_FINISH)) {
            resultMap.put("backMsg", "自然量微信小游戏, 新手引导回传无法判定, 不回传");
            return Tuple2.with(Boolean.FALSE, null);
        }
        //自然量微信小游戏, 全量回传
        if (Objects.equals(param.getCallBackTypeEnum(), CallBackTypeEnum.CALL_BACK_PAY_ORDER)) {
            PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(param.getOrderId());
            if (this.orderPayAndBackCheck(platformOrderDTO, param.getMipeiSuces())) {
                resultMap.put("callBack", Boolean.FALSE);
                resultMap.put("backMsg", "微信小游戏自然量订单未支付或者已回传");
            } else {
                resultMap.put("callBack", Boolean.TRUE);
                resultMap.put("backMsg", "微信小游戏自然量订单全量回传");
                resultMap.put("amount", Collections.singletonList(platformOrderDTO.getAmount().longValue() * 100));
            }
            return Tuple2.with(Boolean.FALSE, null);
        }
        //其他回传, 还是走判断
        return Tuple2.with(Boolean.TRUE, null);
    }

    private boolean orderPayAndBackCheck(PlatformOrderDTO platformOrderDTO, Boolean miPaySuccess) {
        //未上线新版本, 直接不检测, 或者是米大师的成功回调, 直接信任
        if (miPaySuccess == null || miPaySuccess) {
            return false;
        }
        //订单是否需要回传, 不需要回传直接返回true
        return !Objects.equals(platformOrderDTO.getStatus(), OrderStatusEnum.SUCCESS_PAY.getValue())
                || super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                .eq(GameBackLogMediaSdk::getGameId, platformOrderDTO.getGameId())
                .eq(GameBackLogMediaSdk::getUserId, platformOrderDTO.getUserId())
                .eq(GameBackLogMediaSdk::getOrderId, platformOrderDTO.getOrderId())
                .eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_PAY_ORDER.getValue())
        ) > 0;
    }

    private void checkCallBack(User user, Agent agent, Map<String, Object> resultMap, CallBackControlParam param) {
        //玩家id
        Long userId = user.getId();
        //游戏id
        Long gameId = user.getGameId();
        //角色id
        String roleId = param.getRoleId();
        //角色等级
        Long level = param.getLevel();
        //回传类型
        CallBackTypeEnum callBackTypeEnum = param.getCallBackTypeEnum();
        //按不同类型判断
        switch (callBackTypeEnum) {
            case CALL_BACK_LOGIN_IN:
                break;
            case CALL_BACK_REGISTER:
            case CALL_BACK_ACTIVATE:
            case CALL_BACK_CREATE_ROLE:
            case CALL_BACK_UPDATE_LEVEL:
                Tuple2<Boolean, String> distinctCheck = this.callBackDistinctCheck(callBackTypeEnum, gameId, userId, roleId, level);
                resultMap.put("callBack", distinctCheck.first);
                resultMap.put("backMsg", distinctCheck.second);
                break;
            case CALL_BACK_ADD_TO_WISHLIST:
            case CALL_BACK_ON_SHARE_TIME_LINE:
            case CALL_BACK_ON_SHARE_APP_MESSAGE:
            case CALL_BACK_VIEW_CONTENT_MALL:
            case CALL_BACK_VIEW_CONTENT_ACTIVITY:
                resultMap.put("callBack", Boolean.TRUE);
                break;
            case CALL_BACK_RE_ACTIVE:
                Tuple3<Boolean, Long, String> tuple = this.callBackReActiveCheck(userId, gameId);
                resultMap.put("callBack", tuple.first);
                resultMap.put("backFlowDay", tuple.second);
                resultMap.put("backMsg", tuple.third);
                break;
            case CALL_BACK_TUTORIAL_FINISH:
                Tuple2<Boolean, String> tuple2 = this.callBackTutorialFinishCheck(roleId, userId, gameId, agent);
                resultMap.put("callBack", tuple2.first);
                resultMap.put("backMsg", tuple2.second);
                break;
            case CALL_BACK_PAY_ORDER:
                Tuple3<Boolean, List<Long>, String> tuple3 = this.callBackOrderCheck(param.getOrderId(),
                        param.getMipeiSuces(), user, agent);
                resultMap.put("callBack", tuple3.first);
                resultMap.put("amount", tuple3.second);
                resultMap.put("backMsg", tuple3.third);
                break;
            default:
                resultMap.put("backMsg", "未知的回传类型");
        }
    }

    private Tuple2<Boolean, String> callBackDistinctCheck(CallBackTypeEnum callBackTypeEnum, Long gameId, Long userId,
                                                          String roleId, Long level) {
        //注册回传去重判断
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_REGISTER)
                && super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                .eq(GameBackLogMediaSdk::getGameId, gameId)
                .eq(GameBackLogMediaSdk::getUserId, userId)
                .eq(GameBackLogMediaSdk::getCallBackTypeEnum, callBackTypeEnum.getValue())
        ) > 0) {
            return Tuple2.with(Boolean.FALSE, "当前用户注册已回传, 不重复执行");
        }
        //创角回传去重和前置判断
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_ACTIVATE)
                || Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_CREATE_ROLE)) {
            //创角是否已回传, 已回传则不重复执行
            if (super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                    .eq(GameBackLogMediaSdk::getGameId, gameId)
                    .eq(GameBackLogMediaSdk::getUserId, userId)
                    .eq(GameBackLogMediaSdk::getRoleId, roleId)
                    .eq(GameBackLogMediaSdk::getCallBackTypeEnum, callBackTypeEnum.getValue())
            ) > 0) {
                return Tuple2.with(Boolean.FALSE, "当前角色创角已回传, 不重复执行");
            }
            //腾讯小游戏的创角回传, 可以判断前置注册回传是否已执行, 未执行无法回传
            if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_CREATE_ROLE)
                    && super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                    .eq(GameBackLogMediaSdk::getGameId, gameId)
                    .eq(GameBackLogMediaSdk::getUserId, userId)
                    .eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_REGISTER.getValue())
            ) <= 0) {
                return Tuple2.with(Boolean.FALSE, "当前用户注册未回传, 无法执行创角回传");
            }
        }
        //等级提升回传去重和前置条件判断
        if (Objects.equals(callBackTypeEnum, CallBackTypeEnum.CALL_BACK_UPDATE_LEVEL)) {
            //当前等级是否已回传, 已回传则不重复执行
            if (super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                    .eq(GameBackLogMediaSdk::getGameId, gameId)
                    .eq(GameBackLogMediaSdk::getUserId, userId)
                    .eq(GameBackLogMediaSdk::getRoleId, roleId)
                    .eq(GameBackLogMediaSdk::getLevel, level)
                    .eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_UPDATE_LEVEL.getValue())
            ) > 0) {
                return Tuple2.with(Boolean.FALSE, "角色当前等级已回传, 不重复执行");
            }
            //腾讯小游戏的等级提升回传, 可以判断前置创角回传是否已执行, 未执行无法回传
            if (super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                    .eq(GameBackLogMediaSdk::getGameId, gameId)
                    .eq(GameBackLogMediaSdk::getUserId, userId)
                    .eq(GameBackLogMediaSdk::getRoleId, roleId)
                    .eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_CREATE_ROLE.getValue())
            ) <= 0) {
                return Tuple2.with(Boolean.FALSE, "角色创角未回传, 无法执行等级提升回传");
            }
        }
        return Tuple2.with(Boolean.TRUE, "执行回传");
    }

    private Tuple3<Boolean, Long, String> callBackReActiveCheck(Long userId, Long gameId) {
        //判断玩家今日是否已经沉默唤起回传
        if (super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                .eq(GameBackLogMediaSdk::getGameId, gameId)
                .eq(GameBackLogMediaSdk::getUserId, userId)
                .eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_RE_ACTIVE.getValue())
                .ge(GameBackLogMediaSdk::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
        ) > 0) {
            return Tuple3.with(Boolean.FALSE, null, "今日已执行沉默唤起回传, 不重复执行");
        }
        //获取玩家今天之前最后一次登录日志
        UserLoginLog userLoginLog = userLoginLogService.getOne(new LambdaQueryWrapper<UserLoginLog>()
                .eq(UserLoginLog::getUserId, userId)
                .lt(UserLoginLog::getCreateTime, LocalDateTime.of(LocalDate.now(), LocalTime.MIN))
                .orderByDesc(UserLoginLog::getCreateTime)
                .last("limit 1"));
        if (userLoginLog == null) {
            return Tuple3.with(Boolean.FALSE, null, "不存在今日之前的登录日志, 不执行沉默唤起回传");
        }
        //最长未登录天数, 判断标准暂定 30 天
        long backFlowDay = 30;
        long betweenDay = ChronoUnit.DAYS.between(userLoginLog.getCreateTime().toLocalDate(), LocalDate.now());
        //返回判断结果
        return Tuple3.with(betweenDay >= backFlowDay, backFlowDay, "沉默唤起判断依据登录日志id: " + userLoginLog.getId());
    }

    private Tuple2<Boolean, String> callBackTutorialFinishCheck(String roleId, Long userId, Long gameId, Agent agent) {
        //判断创角是否回传, 新手引导回传必须在创角回传之后执行
        if (super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                .eq(GameBackLogMediaSdk::getGameId, gameId)
                .eq(GameBackLogMediaSdk::getUserId, userId)
                .eq(GameBackLogMediaSdk::getRoleId, roleId)
                .and(qw -> qw.eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_CREATE_ROLE.getValue())
                        .or().eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_ACTIVATE.getValue()))
        ) <= 0) {
            return Tuple2.with(Boolean.FALSE, "创角回传未执行, 无法执行新手引导回传");
        }
        //判断用户是否已经执行过新手引导回传, 新手引导回传以用户为回传单位, 不以角色为回传单位
        if (super.count(new LambdaQueryWrapper<GameBackLogMediaSdk>()
                .eq(GameBackLogMediaSdk::getGameId, gameId)
                .eq(GameBackLogMediaSdk::getUserId, userId)
                .eq(GameBackLogMediaSdk::getCallBackTypeEnum, CallBackTypeEnum.CALL_BACK_TUTORIAL_FINISH.getValue())
        ) > 0) {
            return Tuple2.with(Boolean.FALSE, "角色已执行新手引导回传, 不重复执行");
        }
        //新手引导回传线程锁
        String lockKey = RedisKeyConstant.CALL_BACK_TUTORIAL_FINISH_LOCK + userId + "_" + roleId;
        if (!distributedLockComponent.doLock(lockKey, 0L, 30L, TimeUnit.SECONDS)) {
            return Tuple2.with(Boolean.FALSE, "触发新手引导回传线程锁, 不执行");
        }
        //渠道关联的策略
        Long backPolicyId = agent.getBackPolicyId();
        //判断角色当前等级是否满足策略限制等级
        if (backPolicyId == null) {
            return Tuple2.with(Boolean.FALSE, "渠道未绑定策略, 无法执行新手引导回传, agentId : " + agent.getId());
        }
        //查询策略信息
        Map<Long, GameBackPolicyRpcVO> policyRpcVOMap = gameBackPolicyRpc.listByIds(
                Collections.singletonList(backPolicyId)).getData();
        GameBackPolicyRpcVO gameBackPolicyRpcVO = policyRpcVOMap.get(backPolicyId);
        if (gameBackPolicyRpcVO == null || Strings.isBlank(gameBackPolicyRpcVO.getTutorialFinishJson())) {
            return Tuple2.with(Boolean.FALSE, "渠道绑定的策略信息不存在, 或者未设置新手引导回传等级, 无法执行新手引导回传, agentId : " + agent.getId());
        }
        JSONObject tutorialFinish = JSONObject.parse(gameBackPolicyRpcVO.getTutorialFinishJson());
        Long roleLevel = tutorialFinish.getLong("roleLevel");
        if (roleLevel == null) {
            return Tuple2.with(Boolean.FALSE, "新手引导回传等级roleLevel设置为空, 无法执行新手引导回传, agentId : " + agent.getId());
        }
        //查询角色当前等级
        GameUserRole gameUserRole = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
                .eq(GameUserRole::getUserId, userId)
                .eq(GameUserRole::getGameId, gameId)
                .eq(GameUserRole::getRoleId, roleId)
                .orderByDesc(GameUserRole::getCreateTime).last("limit 1"));
        if (gameUserRole == null || gameUserRole.getRoleLevel() == null) {
            return Tuple2.with(Boolean.FALSE, "角色信息或者角色等级信息不存在, 无法执行新手引导回传, roleId : " + roleId);
        }
        //等级判断
        if (gameUserRole.getRoleLevel() < roleLevel) {
            return Tuple2.with(Boolean.FALSE, "角色等级未达到策略等级限制要求, 不执行新手引导回传, agentId : " + agent.getId());
        }
        //返回执行新手引导回传
        return Tuple2.with(Boolean.TRUE, "执行新手引导回传");
    }

    private Tuple3<Boolean, List<Long>, String> callBackOrderCheck(String orderId, Boolean miPaySuccess, User user, Agent agent) {
        Boolean doBack = Boolean.FALSE;
        List<Long> amount = null;
        String backMsg = null;
        //头条APP直投回传
        if (Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE_APP.getValue())) {
            TtAppOrderBackQueryRpcDTO orderQuery = this.transform(user.getId(), orderId, agent);
            OrderBackQueryRpcVO orderBackQueryRpcVO = ttAppBackRpc.orderBackQuery(orderQuery).getData();
            //传过来的金额是分, 头条媒体SDK需要换算成元, 存在小数直接舍弃
            if (Objects.equals(orderBackQueryRpcVO.getDoBack(), Boolean.TRUE)) {
                amount = orderBackQueryRpcVO.getBackMoney().stream().map(money -> money / 100).collect(Collectors.toList());
            }
            doBack = orderBackQueryRpcVO.getDoBack();
            backMsg = orderBackQueryRpcVO.getBackMsg();
        }
        //腾讯APP直投回传
        if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_APP.getValue())) {
            TencentAppOrderBackQueryRpcDTO orderQuery = this.transform(agent, user.getId(), orderId);
            OrderBackQueryRpcVO orderBackQueryRpcVO = tencentAppBackRpc.orderBackQuery(orderQuery).getData();
            //腾讯回传金额单位为分, 不需要处理
            amount = orderBackQueryRpcVO.getBackMoney();
            doBack = orderBackQueryRpcVO.getDoBack();
            backMsg = orderBackQueryRpcVO.getBackMsg();
        }
        //微信小游戏投放腾讯广告或巨量广告 (坑逼腾讯要其他渠道的订单数据)
        if (Objects.equals(agent.getAccountType(), AccountTypeEnum.TENCENT_MINI_GAME.getValue())
                || Objects.equals(agent.getAccountType(), AccountTypeEnum.BYTE.getValue())) {
            PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(orderId);
            if (this.orderPayAndBackCheck(platformOrderDTO, miPaySuccess)) {
                doBack = Boolean.FALSE;
                backMsg = "微信小游戏投放腾讯广告或巨量广告, 订单未支付或已回传";
            } else {
                doBack = Boolean.TRUE;
                amount = Collections.singletonList(platformOrderDTO.getAmount().longValue() * 100);
                backMsg = "微信小游戏投放腾讯广告或巨量广告, 订单全量满金额回传";
            }
        }
        return Tuple3.with(doBack, amount, Strings.isBlank(backMsg) ? "未知的渠道投放类型" : backMsg);
    }

    @Override
    public boolean addMediaSdkBackLog(BackLogMediaSdkParam param, UserData userData) {
        GameBackLogMediaSdk gameBackLogMediaSdk = this.transform(param, userData);
        Map<String, Object> callBackJudgeResult = param.getCallBackJudgeResult();
        //回传判断结果参数不为空
        if (!callBackJudgeResult.isEmpty()) {
            if (callBackJudgeResult.containsKey("roleId")) {
                gameBackLogMediaSdk.setRoleId(String.valueOf(callBackJudgeResult.get("roleId")));
            }
            if (callBackJudgeResult.containsKey("orderId")) {
                gameBackLogMediaSdk.setOrderId(String.valueOf(callBackJudgeResult.get("orderId")));
            }
            if (callBackJudgeResult.containsKey("level")) {
                gameBackLogMediaSdk.setLevel(Long.parseLong(String.valueOf(callBackJudgeResult.get("level"))));
            }
        }
        return super.save(gameBackLogMediaSdk);
    }

    private GameBackLogMediaSdk transform(BackLogMediaSdkParam param, UserData userData) {
        return GameBackLogMediaSdk.builder()
                .gameId(userData.getGameId())
                .userId(userData.getUserId())
                .callBackTypeEnum(param.getCallBackTypeEnum().getValue())
                .callBackJudgeResult(JsonUtil.toString(param.getCallBackJudgeResult()))
                .callBackParam(JsonUtil.toString(param.getCallBackParam()))
                .errorMsg(param.getErrorMsg())
                .createTime(LocalDateTime.now())
                .build();
    }

    private TtAppOrderBackQueryRpcDTO transform(Long userId, String orderId, Agent agent) {
        return TtAppOrderBackQueryRpcDTO.builder()
                .gameId(agent.getGameId())
                .userId(userId.toString())
                .orderId(orderId)
                .agentKey(agent.getAgentKey())
                .build();
    }

    private TencentAppOrderBackQueryRpcDTO transform(Agent agent, Long userId, String orderId) {
        return TencentAppOrderBackQueryRpcDTO.builder()
                .gameId(agent.getGameId())
                .userId(userId.toString())
                .orderId(orderId)
                .agentKey(agent.getAgentKey())
                .build();
    }
}