package com.zanxiang.game.back.serve.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.sd4324530.jtuple.Tuple2;
import com.github.sd4324530.jtuple.Tuple4;
import com.zanxiang.game.back.base.pojo.enums.OrderStatusEnum;
import com.zanxiang.game.back.serve.dao.mapper.GameOceanengineAppOrderLogMapper;
import com.zanxiang.game.back.serve.oceanengine.MiniGameCallback;
import com.zanxiang.game.back.serve.pojo.dto.GameOceanengineAppOrderLogDTO;
import com.zanxiang.game.back.serve.pojo.entity.*;
import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
import com.zanxiang.game.back.serve.pojo.enums.BackUnitEnum;
import com.zanxiang.game.back.serve.pojo.vo.GameOceanengineAppOrderLogVO;
import com.zanxiang.game.back.serve.pojo.vo.GameOceanengineAppOrderSplitLogVO;
import com.zanxiang.game.back.serve.service.*;
import com.zanxiang.game.back.serve.utils.BackPolicyUtil;
import com.zanxiang.game.back.serve.utils.OrderUtil;
import com.zanxiang.game.module.base.ServerInfo;
import com.zanxiang.game.module.base.pojo.vo.AgentRpcVO;
import com.zanxiang.game.module.base.rpc.IAgentRpc;
import com.zanxiang.module.util.ObjectUtil;
import com.zanxiang.module.util.bean.BeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GameOceanengineAppOrderLogServiceImpl extends ServiceImpl<GameOceanengineAppOrderLogMapper, GameOceanengineAppOrderLog>
        implements IGameOceanengineAppOrderLogService {

    @Autowired
    private IGameBackPolicyService gameBackPolicyService;
    @Autowired
    private IGameOceanengineAppUserLogService gameOceanengineAppUserLogService;
    @Autowired
    private IGameOceanengineAppBackLogService gameOceanengineAppBackLogService;
    @Autowired
    private IGameOceanengineAppOrderSplitLogService gameOceanengineAppOrderSplitLogService;
    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private IAgentRpc agentRpc;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean callback(GameOceanengineAppOrderLog orderLog) {
        if (!Objects.equals(orderLog.getOrderStatus(), OrderStatusEnum.SUCCESS_PAY.getValue())) {
            // 头条只要回传支付订单
            return true;
        }
        GameOceanengineAppUserLog userLog = userLog(orderLog);
        if (userLog == null) {
            // 找不到用户
            return update(new LambdaUpdateWrapper<GameOceanengineAppOrderLog>()
                    .set(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.NO.getBackStatus())
                    .set(GameOceanengineAppOrderLog::getBackMoney, orderLog.getAmount())
                    .set(GameOceanengineAppOrderLog::getBackMsg, "回传失败！找不到回传用户")
                    .eq(GameOceanengineAppOrderLog::getId, orderLog.getId())
            );
        }

        GameBackPolicy gameBackPolicy = gameBackPolicyService.getById(orderLog.getBackPolicyId());
        Tuple4<Boolean, Long, String, List<Tuple2<Long, LocalDateTime>>> backInfo = BackPolicyUtil.backOrder(orderLog.getOrderNo(), gameBackPolicy, orderLog.getAmount(),
                orderLog.getIsFirstOrder(),
                orderLog.getPayTime(),
                // 此处使用用户最近一次的重新染色时间
                userLog.getCreateTime(),
                orderLog.getUserId(),
                new OceanengineOrderBackPolicyCheck(this, gameOceanengineAppOrderSplitLogService, userLog, orderLog, gameBackPolicy)
        );
        boolean doBack = backInfo.first;
        Long backMoney = backInfo.second;
        String backMsg = backInfo.third;

        if (!doBack) {
            // 不回传
            return update(new LambdaUpdateWrapper<GameOceanengineAppOrderLog>()
                    .set(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.NO.getBackStatus())
                    .set(GameOceanengineAppOrderLog::getBackMoney, backMoney)
                    .set(GameOceanengineAppOrderLog::getBackMsg, backMsg)
                    .eq(GameOceanengineAppOrderLog::getId, orderLog.getId())
            );
        }
        // 拆单
        if (CollectionUtils.isNotEmpty(backInfo.fourth)) {
            // 需要拆单
            List<GameOceanengineAppOrderSplitLog> splitOrderLogList = new ArrayList<>(backInfo.fourth.size());
            for (int i = 0; i < backInfo.fourth.size(); i++) {
                Tuple2<Long, LocalDateTime> splitOrder = backInfo.fourth.get(i);
                splitOrderLogList.add(GameOceanengineAppOrderSplitLog.builder()
                        .backDay(splitOrder.second.toLocalDate())
                        .orderNo(orderLog.getOrderNo())
                        .backIndex(i + 1)
                        .backCount(backInfo.fourth.size())
                        .splitMoney(splitOrder.first)
                        .backTime(splitOrder.second)
                        .backStatus(BackStatusEnum.SUCCESS.getBackStatus())
                        .createTime(LocalDateTime.now())
                        .build()
                );
            }
            gameOceanengineAppOrderSplitLogService.saveBatch(splitOrderLogList);
            return update(new LambdaUpdateWrapper<GameOceanengineAppOrderLog>()
                    .set(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                    .set(GameOceanengineAppOrderLog::getBackMoney, backInfo.second)
                    .set(GameOceanengineAppOrderLog::getBackMsg, backMsg)
                    .eq(GameOceanengineAppOrderLog::getId, orderLog.getId())
            );
        }

        Tuple2<BackStatusEnum, String> backResult = doCallback(orderLog, orderLog.getPayTime(), backMoney);
        if (StringUtils.isNotBlank(backResult.second)) {
            backMsg = backMsg + ("回传失败：" + backResult.second);
        }
        return update(new LambdaUpdateWrapper<GameOceanengineAppOrderLog>()
                .set(GameOceanengineAppOrderLog::getBackStatus, backResult.first.getBackStatus())
                .set(GameOceanengineAppOrderLog::getBackMoney, backMoney)
                .set(GameOceanengineAppOrderLog::getBackMsg, backMsg)
                .eq(GameOceanengineAppOrderLog::getId, orderLog.getId())
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Tuple2<BackStatusEnum, String> doCallback(GameOceanengineAppOrderLog orderLog, LocalDateTime backTime, Long backMoney) {
        GameOceanengineAppUserLog userLog = userLog(orderLog);
        if (userLog == null) {
            gameOceanengineAppBackLogService.save(GameOceanengineAppBackLog.builder()
                    .userId(orderLog.getUserId())
                    .mac(orderLog.getMac())
                    .imei(orderLog.getImei())
                    .oaid(orderLog.getOaid())
                    .androidId(orderLog.getAndroidId())
                    .idfa(orderLog.getIdfa())
                    .caid(orderLog.getCaid())
                    .gameId(orderLog.getGameId())
                    .accountId(orderLog.getAccountId())
                    .eventType(String.valueOf(MiniGameCallback.EventType.ACTIVE_PAY.getEventType()))
                    .eventLogId(orderLog.getId())
                    .createTime(LocalDateTime.now())
                    .payAmount(backMoney)
                    .backStatus(BackStatusEnum.FAILED.getBackStatus())
                    .errMsg("找不到用户注册信息")
                    .build());
            return Tuple2.with(BackStatusEnum.FAILED, "找不到用户注册信息");
        }
        Integer eventType = MiniGameCallback.EventType.ACTIVE_PAY.getEventType();
        BackStatusEnum backStatus = BackStatusEnum.SUCCESS;
        String errMsg = null;
        gameOceanengineAppBackLogService.save(GameOceanengineAppBackLog.builder()
                .userId(orderLog.getUserId())
                .mac(orderLog.getMac())
                .imei(orderLog.getImei())
                .oaid(orderLog.getOaid())
                .androidId(orderLog.getAndroidId())
                .idfa(orderLog.getIdfa())
                .caid(orderLog.getCaid())
                .gameId(orderLog.getGameId())
                .accountId(orderLog.getAccountId())
                .eventType(String.valueOf(eventType))
                .eventLogId(orderLog.getId())
                .createTime(LocalDateTime.now())
                .payAmount(backMoney)
                .backStatus(backStatus.getBackStatus())
                .errMsg(errMsg)
                .build());
        return Tuple2.with(backStatus, errMsg);
    }

    @Override
    public IPage<GameOceanengineAppOrderLogVO> oceanengineAppOrderLogList(GameOceanengineAppOrderLogDTO dto) {
        IPage<GameOceanengineAppOrderLog> page = page(dto.toPage(), new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                .eq(StringUtils.isNotBlank(dto.getUserId()), GameOceanengineAppOrderLog::getUserId, dto.getUserId())
                .eq(StringUtils.isNotBlank(dto.getMac()), GameOceanengineAppOrderLog::getMac, dto.getMac())
                .eq(StringUtils.isNotBlank(dto.getImei()), GameOceanengineAppOrderLog::getImei, dto.getImei())
                .eq(StringUtils.isNotBlank(dto.getOaid()), GameOceanengineAppOrderLog::getOaid, dto.getOaid())
                .eq(StringUtils.isNotBlank(dto.getAndroidId()), GameOceanengineAppOrderLog::getAndroidId, dto.getAndroidId())
                .eq(StringUtils.isNotBlank(dto.getIdfa()), GameOceanengineAppOrderLog::getIdfa, dto.getIdfa())
                .eq(StringUtils.isNotBlank(dto.getCaid()), GameOceanengineAppOrderLog::getCaid, dto.getCaid())
                .eq(StringUtils.isNotBlank(dto.getAgentKey()), GameOceanengineAppOrderLog::getAgentKey, dto.getAgentKey())
                .ge(dto.getRegTimeBegin() != null, GameOceanengineAppOrderLog::getRegTime, dto.getRegTimeBegin() == null ? null : LocalDateTime.of(dto.getRegTimeBegin(), LocalTime.MIDNIGHT))
                .le(dto.getRegTimeEnd() != null, GameOceanengineAppOrderLog::getRegTime, dto.getRegTimeEnd() == null ? null : LocalDateTime.of(dto.getRegTimeEnd(), LocalTime.MAX))
                .eq(null != dto.getGameId(), GameOceanengineAppOrderLog::getGameId, dto.getGameId())
                .eq(null != dto.getAccountId(), GameOceanengineAppOrderLog::getAccountId, dto.getAccountId())
                .eq(StringUtils.isNotBlank(dto.getOrderNo()), GameOceanengineAppOrderLog::getOrderNo, dto.getOrderNo())
                .eq(null != dto.getBackStatus(), GameOceanengineAppOrderLog::getBackStatus, dto.getBackStatus())
                .eq(dto.getIsFirstOrder() != null, GameOceanengineAppOrderLog::getIsFirstOrder, dto.getIsFirstOrder())
                .eq(null != dto.getOrderStatus(), GameOceanengineAppOrderLog::getOrderStatus, dto.getOrderStatus())
                .like(StringUtils.isNotBlank(dto.getRoleName()), GameOceanengineAppOrderLog::getRoleName, dto.getRoleName())
                .apply(dto.getRegPayIntervalTimeMin() != null, "if(pay_time is not null, TIMESTAMPDIFF(minute, reg_time, pay_time), null) >= {0}", dto.getRegPayIntervalTimeMin())
                .apply(dto.getRegPayIntervalTimeMax() != null, "if(pay_time is not null, TIMESTAMPDIFF(minute, reg_time, pay_time), null) <= {0}", dto.getRegPayIntervalTimeMax())
                .orderByDesc(GameOceanengineAppOrderLog::getCreateTime)
        );
        IPage<GameOceanengineAppOrderLogVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return result;
        }
        result.setRecords(toVOBatch(page.getRecords()));
        return result;
    }

    private List<GameOceanengineAppOrderLogVO> toVOBatch(List<GameOceanengineAppOrderLog> logList) {
        if (CollectionUtils.isEmpty(logList)) {
            return Collections.emptyList();
        }
        List<GameOceanengineAppOrderSplitLogVO> orderSplitList = gameOceanengineAppOrderSplitLogService.listByOrderNo(logList.stream().map(GameOceanengineAppOrderLog::getOrderNo).collect(Collectors.toSet()));
        Set<String> agentKeys = logList.stream().map(GameOceanengineAppOrderLog::getAgentKey).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
        Map<String, AgentRpcVO> agentMap = CollectionUtils.isEmpty(agentKeys) ? Collections.emptyMap() : agentRpc.getByAgentKeys(new ArrayList<>(agentKeys))
                .getData().stream().collect(Collectors.toMap(AgentRpcVO::getAgentKey, Function.identity()));
        return logList.stream().map(log -> {
            GameOceanengineAppOrderLogVO vo = toVOSimple(log);
            if (StringUtils.isNotBlank(log.getAgentKey())) {
                AgentRpcVO agent = agentMap.get(log.getAgentKey());
                if (agent != null) {
                    vo.setAgentName(agent.getAgentName());
                }

            }
            List<GameOceanengineAppOrderSplitLogVO> splitList = orderSplitList.stream().filter(obj -> obj.getOrderNo().equals(log.getOrderNo())).collect(Collectors.toList());
            vo.setSplitOrderList(splitList);
            return vo;
        }).collect(Collectors.toList());

    }

    private GameOceanengineAppOrderLogVO toVOSimple(GameOceanengineAppOrderLog log) {
        if (log == null) {
            return null;
        }
        GameOceanengineAppOrderLogVO vo = BeanUtil.copy(log, GameOceanengineAppOrderLogVO.class);
        vo.setRegPayIntervalTime(OrderUtil.regPayIntervalTime(log.getPayTime(), log.getRegTime()));
        return vo;
    }

    private GameOceanengineAppUserLog userLog(GameOceanengineAppOrderLog orderLog) {
        return gameOceanengineAppUserLogService.getOne(new LambdaQueryWrapper<GameOceanengineAppUserLog>()
                .eq(GameOceanengineAppUserLog::getGameId, orderLog.getGameId())
                .eq(GameOceanengineAppUserLog::getUserId, orderLog.getUserId())
                .eq(GameOceanengineAppUserLog::getAccountId, orderLog.getAccountId())
                .orderByDesc(GameOceanengineAppUserLog::getCreateTime)
                .last("limit 1")
        );
    }

    public static class OceanengineOrderBackPolicyCheck implements BackPolicyUtil.IBackPolicyCheck {
        private final IGameOceanengineAppOrderLogService gameOceanengineAppOrderLogService;
        private final IGameOceanengineAppOrderSplitLogService gameOceanengineAppOrderSplitLogService;
        private final GameBackPolicy gameBackPolicy;
        private final GameOceanengineAppUserLog userLog;
        private final GameOceanengineAppOrderLog orderLog;

        public OceanengineOrderBackPolicyCheck(IGameOceanengineAppOrderLogService gameOceanengineAppOrderLogService, IGameOceanengineAppOrderSplitLogService gameOceanengineAppOrderSplitLogService, GameOceanengineAppUserLog userLog, GameOceanengineAppOrderLog orderLog, GameBackPolicy gameBackPolicy) {
            this.gameOceanengineAppOrderLogService = gameOceanengineAppOrderLogService;
            this.gameOceanengineAppOrderSplitLogService = gameOceanengineAppOrderSplitLogService;
            this.gameBackPolicy = gameBackPolicy;
            this.userLog = userLog;
            this.orderLog = orderLog;
        }

        public Tuple2<Long, Long> backCountForUserWithSplitOrder(String userId) {
            Map<String, Long> orderMap = gameOceanengineAppOrderLogService.list(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                    .select(GameOceanengineAppOrderLog::getOrderNo, GameOceanengineAppOrderLog::getBackMoney)
                    .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                    .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                    .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                    .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                    .eq(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                    .eq(GameOceanengineAppOrderLog::getUserId, userId)
                    .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
            ).stream().collect(Collectors.toMap(GameOceanengineAppOrderLog::getOrderNo, GameOceanengineAppOrderLog::getBackMoney));
            if (MapUtils.isEmpty(orderMap)) {
                return Tuple2.with(0L, 0L);
            }
            Map<String, Integer> splitBackCountMap = gameOceanengineAppOrderSplitLogService.list(new LambdaQueryWrapper<GameOceanengineAppOrderSplitLog>()
                    .select(GameOceanengineAppOrderSplitLog::getOrderNo, GameOceanengineAppOrderSplitLog::getBackIndex, GameOceanengineAppOrderSplitLog::getBackCount)
                    .in(GameOceanengineAppOrderSplitLog::getOrderNo, orderMap.keySet())
            ).stream().collect(Collectors.toMap(GameOceanengineAppOrderSplitLog::getOrderNo, GameOceanengineAppOrderSplitLog::getBackCount, (obj1, obj2) -> obj1));
            long count = 0;
            long backMoney = 0;
            for (Map.Entry<String, Long> entry : orderMap.entrySet()) {
                Integer splitBackCount = splitBackCountMap.get(entry.getKey());
                backMoney += entry.getValue();
                if (splitBackCount != null) {
                    count += splitBackCount;
                } else {
                    count += 1;
                }
            }
            return Tuple2.with(count, backMoney);
        }

        @Override
        public long backCountForFixedRate(int numberOfRound, BackUnitEnum backUnit, Boolean firstPolicy) {
            numberOfRound = numberOfRound - 1;
            if (backUnit == BackUnitEnum.UNIT_ONCE) {
                return gameOceanengineAppOrderLogService.list(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .eq(GameOceanengineAppOrderLog::getIsFirstOrder, firstPolicy)
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                        .orderByDesc(GameOceanengineAppOrderLog::getCreateTime)
                        .last("limit " + numberOfRound)
                ).stream().filter(log -> log.getBackStatus().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
            } else if (backUnit == BackUnitEnum.UNIT_DAY) {
                return gameOceanengineAppOrderLogService.list(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .apply(firstPolicy, "date(reg_time) = date(pay_time)")
                        .apply(!firstPolicy, "date(reg_time) != date(pay_time)")
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                        .orderByDesc(GameOceanengineAppOrderLog::getCreateTime)
                        .last("limit " + numberOfRound)
                ).stream().filter(log -> log.getBackStatus().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
            } else if (backUnit == BackUnitEnum.UNIT_TIME_DAY) {
                return gameOceanengineAppOrderLogService.list(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, reg_time, pay_time) < 24")
                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, reg_time, pay_time) >= 24")
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                        .orderByDesc(GameOceanengineAppOrderLog::getCreateTime)
                        .last("limit " + numberOfRound)
                ).stream().filter(log -> log.getBackStatus().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
            } else if (backUnit == BackUnitEnum.UNIT_TIME_2DAY) {
                return gameOceanengineAppOrderLogService.list(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, reg_time, pay_time) < 48")
                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, reg_time, pay_time) >= 48")
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                        .orderByDesc(GameOceanengineAppOrderLog::getCreateTime)
                        .last("limit " + numberOfRound)
                ).stream().filter(log -> log.getBackStatus().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
            } else if (backUnit == BackUnitEnum.LARGE_AMOUNT) {
                return gameOceanengineAppOrderLogService.list(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                        .orderByDesc(GameOceanengineAppOrderLog::getCreateTime)
                        .last("limit " + numberOfRound)
                ).stream().filter(log -> log.getBackStatus().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
            } else {
                throw new RuntimeException("不支持的回传单位[" + backUnit.getValue() + "]");
            }
        }

        @Override
        public long backCountForUser(BackUnitEnum backUnit, String userId, Boolean firstPolicy) {
            if (backUnit == BackUnitEnum.UNIT_ONCE) {
                if (firstPolicy) {
                    // 首单直接返回 0，必定回传
                    return 0;
                }
                return gameOceanengineAppOrderLogService.count(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .eq(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                        .eq(GameOceanengineAppOrderLog::getUserId, userId)
                        .eq(GameOceanengineAppOrderLog::getIsFirstOrder, firstPolicy)
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                );
            } else if (backUnit == BackUnitEnum.UNIT_DAY) {
                return gameOceanengineAppOrderLogService.count(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .eq(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                        .eq(GameOceanengineAppOrderLog::getUserId, userId)
                        .apply(firstPolicy, "date(reg_time) = date(pay_time)")
                        .apply(!firstPolicy, "date(reg_time) != date(pay_time)")
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                );
            } else if (backUnit == BackUnitEnum.UNIT_TIME_DAY) {
                return gameOceanengineAppOrderLogService.count(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .eq(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                        .eq(GameOceanengineAppOrderLog::getUserId, userId)
                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, reg_time, pay_time) < 24")
                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, reg_time, pay_time) >= 24")
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                );
            } else if (backUnit == BackUnitEnum.UNIT_TIME_2DAY) {
                return gameOceanengineAppOrderLogService.count(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .eq(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                        .eq(GameOceanengineAppOrderLog::getUserId, userId)
                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, reg_time, pay_time) < 48")
                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, reg_time, pay_time) >= 48")
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                );
            } else if (backUnit == BackUnitEnum.LARGE_AMOUNT) {
                return gameOceanengineAppOrderLogService.count(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                        .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                        .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                        .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                        .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                        .eq(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                        .eq(GameOceanengineAppOrderLog::getUserId, userId)
                        .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                );
            } else {
                throw new RuntimeException("不支持的回传单位[" + backUnit.getValue() + "]");
            }
        }

        @Override
        public long markUpOfFixedRate(int numberOfRound, Long markUpTime) {
            numberOfRound = numberOfRound - 1;
            return gameOceanengineAppOrderLogService.list(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                    .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                    .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                    .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                    .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                    .apply("TIMESTAMPDIFF(MINUTE, reg_time, pay_time) > {0}", markUpTime)
                    .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
                    .orderByDesc(GameOceanengineAppOrderLog::getCreateTime)
                    .last("limit " + numberOfRound)
            ).stream().filter(log -> log.getBackStatus().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
        }

        @Override
        public long markUpForUser(String userId, Long markUpTime) {
            return gameOceanengineAppOrderLogService.count(new LambdaQueryWrapper<GameOceanengineAppOrderLog>()
                    .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                    .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                    .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                    .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                    .eq(GameOceanengineAppOrderLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                    .eq(GameOceanengineAppOrderLog::getUserId, userId)
                    .apply("TIMESTAMPDIFF(MINUTE, reg_time, pay_time) > {0}", markUpTime)
                    .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
            );
        }

        @Override
        public Long totalRechargeAmount() {
            Map<String, Object> data = gameOceanengineAppOrderLogService.getMap(new QueryWrapper<GameOceanengineAppOrderLog>()
                    .select("ifnull(sum(amount), 0) as amount").lambda()
                    .eq(GameOceanengineAppOrderLog::getGameId, orderLog.getGameId())
                    .eq(GameOceanengineAppOrderLog::getAccountId, orderLog.getAccountId())
                    .eq(GameOceanengineAppOrderLog::getBackPolicyId, gameBackPolicy.getId())
                    .eq(GameOceanengineAppOrderLog::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                    .eq(GameOceanengineAppOrderLog::getUserId, userLog.getUserId())
                    .ne(GameOceanengineAppOrderLog::getOrderNo, orderLog.getOrderNo())
            );
            return ObjectUtil.objToLong(data.get("amount"), 0L);
        }
    }
}
