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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.github.sd4324530.jtuple.Tuple3;
import com.zanxiang.advertising.tencent.base.AdvertisingTencentServer;
import com.zanxiang.advertising.tencent.base.pojo.dto.DataReportOfAppIdRpcDTO;
import com.zanxiang.advertising.tencent.base.pojo.dto.UserActionRpcDTO;
import com.zanxiang.advertising.tencent.base.rpc.IUserActionSetRpc;
import com.zanxiang.erp.base.ErpServer;
import com.zanxiang.erp.base.rpc.ISysUserRpc;
import com.zanxiang.game.back.base.pojo.enums.OrderStatusEnum;
import com.zanxiang.game.back.serve.pojo.dto.GameTencentOrderDTO;
import com.zanxiang.game.back.serve.pojo.entity.GameBackPolicy;
import com.zanxiang.game.back.serve.pojo.entity.GameTencentBackLog;
import com.zanxiang.game.back.serve.pojo.entity.GameTencentOrder;
import com.zanxiang.game.back.serve.dao.mapper.GameTencentOrderMapper;
import com.zanxiang.game.back.serve.pojo.enums.ActionTypeEnum;
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.GameTencentOrderVO;
import com.zanxiang.game.back.serve.service.IGameBackPolicyService;
import com.zanxiang.game.back.serve.service.IGameTencentBackLogService;
import com.zanxiang.game.back.serve.service.IGameTencentOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.bean.BeanUtil;
import com.zanxiang.module.util.pojo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
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.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 游戏腾讯订单表 服务实现类
 * </p>
 *
 * @author auto
 * @since 2023-06-01
 */
@Slf4j
@Service
public class GameTencentOrderServiceImpl extends ServiceImpl<GameTencentOrderMapper, GameTencentOrder> implements IGameTencentOrderService {
    @DubboReference(providedBy = AdvertisingTencentServer.SERVER_DUBBO_NAME)
    private IUserActionSetRpc userActionSetRpc;
    @DubboReference(providedBy = ErpServer.SERVER_DUBBO_NAME)
    private ISysUserRpc sysUserRpc;
    @Autowired
    private IGameTencentBackLogService gameTencentBackLogService;
    @Autowired
    private IGameBackPolicyService gameBackPolicyService;
    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private IAgentRpc agentRpc;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean orderBack(GameTencentOrder orderLog, boolean mustBack, Long backMoneyOfInput) {
        boolean doBack = false;
        Long backMoney;
        String backMsg;
        if (mustBack) {
            doBack = true;
            backMoney = backMoneyOfInput;
            backMsg = "手动触发的回传";
        } else {
            if (!Objects.equals(orderLog.getOrderStatus(), OrderStatusEnum.SUCCESS_PAY.getValue())) {
                // 只要回传 支付行为
                return false;
            }
            GameBackPolicy gameBackPolicy = gameBackPolicyService.getById(orderLog.getBackPolicyId());
            Tuple3<Boolean, Long, String> backInfo = BackPolicyUtil.backOrder(orderLog.getOrderId(), gameBackPolicy, orderLog.getRechargeMoney(),
                    orderLog.getIsFirstOrder(), orderLog.getPayTime(), orderLog.getRegisterTime(),
                    orderLog.getWechatOpenid(),
                    new BackPolicyUtil.IBackPolicyCheck() {
                        @Override
                        public long backCountForFixedRate(int numberOfRound, BackUnitEnum backUnit, Boolean firstPolicy) {
                            numberOfRound = numberOfRound - 1;
                            if (backUnit == BackUnitEnum.UNIT_ONCE) {
                                return list(new LambdaQueryWrapper<GameTencentOrder>()
                                        .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                        .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                        .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                        .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                        .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                        .eq(GameTencentOrder::getIsFirstOrder, firstPolicy)
                                        .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                                        .orderByDesc(GameTencentOrder::getCreateTime)
                                        .last("limit " + numberOfRound)
                                ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
                            } else if (backUnit == BackUnitEnum.UNIT_DAY) {
                                return list(new LambdaQueryWrapper<GameTencentOrder>()
                                        .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                        .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                        .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                        .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                        .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                        .apply(firstPolicy, "date(recharge_time) = date(pay_time)")
                                        .apply(!firstPolicy, "date(recharge_time) != date(pay_time)")
                                        .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                                        .orderByDesc(GameTencentOrder::getCreateTime)
                                        .last("limit " + numberOfRound)
                                ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
                            } else if (backUnit == BackUnitEnum.UNIT_TIME_DAY) {
                                return list(new LambdaQueryWrapper<GameTencentOrder>()
                                        .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                        .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                        .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                        .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                        .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) < 24")
                                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) >= 24")
                                        .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                                        .orderByDesc(GameTencentOrder::getCreateTime)
                                        .last("limit " + numberOfRound)
                                ).stream().filter(log -> log.getIsBack().equals(BackStatusEnum.SUCCESS.getBackStatus())).count();
                            } else if (backUnit == BackUnitEnum.UNIT_TIME_2DAY) {
                                return list(new LambdaQueryWrapper<GameTencentOrder>()
                                        .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                        .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                        .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                        .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                        .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) < 48")
                                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) >= 48")
                                        .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                                        .orderByDesc(GameTencentOrder::getCreateTime)
                                        .last("limit " + numberOfRound)
                                ).stream().filter(log -> log.getIsBack().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 count(new LambdaQueryWrapper<GameTencentOrder>()
                                        .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                        .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                        .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                        .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                        .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                        .eq(GameTencentOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
                                        .eq(GameTencentOrder::getWechatOpenid, userId)
                                        .eq(GameTencentOrder::getIsFirstOrder, firstPolicy)
                                        .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                                );
                            } else if (backUnit == BackUnitEnum.UNIT_DAY) {
                                return count(new LambdaQueryWrapper<GameTencentOrder>()
                                        .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                        .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                        .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                        .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                        .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                        .eq(GameTencentOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
                                        .eq(GameTencentOrder::getWechatOpenid, userId)
                                        .apply(firstPolicy, "date(recharge_time) = date(pay_time)")
                                        .apply(!firstPolicy, "date(recharge_time) != date(pay_time)")
                                        .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                                );
                            } else if (backUnit == BackUnitEnum.UNIT_TIME_DAY) {
                                return count(new LambdaQueryWrapper<GameTencentOrder>()
                                        .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                        .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                        .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                        .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                        .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                        .eq(GameTencentOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
                                        .eq(GameTencentOrder::getWechatOpenid, userId)
                                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) < 24")
                                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) >= 24")
                                        .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                                );
                            } else if (backUnit == BackUnitEnum.UNIT_TIME_2DAY) {
                                return count(new LambdaQueryWrapper<GameTencentOrder>()
                                        .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                        .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                        .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                        .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                        .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                        .eq(GameTencentOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
                                        .eq(GameTencentOrder::getWechatOpenid, userId)
                                        .apply(firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) < 48")
                                        .apply(!firstPolicy, "TIMESTAMPDIFF(HOUR, recharge_time, pay_time) >= 48")
                                        .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                                );
                            } else {
                                throw new RuntimeException("不支持的回传单位[" + backUnit.getValue() + "]");
                            }
                        }

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

                        @Override
                        public long markUpForUser(String userId, Long markUpTime) {
                            return count(new LambdaQueryWrapper<GameTencentOrder>()
                                    .eq(GameTencentOrder::getGameId, orderLog.getGameId())
                                    .eq(GameTencentOrder::getWechatAppId, orderLog.getWechatAppId())
                                    .eq(GameTencentOrder::getAdAccountId, orderLog.getAdAccountId())
                                    .eq(GameTencentOrder::getBackPolicyId, gameBackPolicy.getId())
                                    .eq(GameTencentOrder::getOrderStatus, OrderStatusEnum.SUCCESS_PAY.getValue())
                                    .eq(GameTencentOrder::getIsBack, BackStatusEnum.SUCCESS.getBackStatus())
                                    .eq(GameTencentOrder::getWechatOpenid, userId)
                                    .apply("TIMESTAMPDIFF(MINUTE, recharge_time, pay_time) > {0}", markUpTime)
                                    .ne(GameTencentOrder::getOrderId, orderLog.getOrderId())
                            );
                        }
                    });
            doBack = backInfo.first;
            backMoney = backInfo.second;
            backMsg = backInfo.third;
        }
        BackStatusEnum backStatus = BackStatusEnum.NO;
        if (doBack) {
            backStatus = doCallback(orderLog);
        }
        return update(new LambdaUpdateWrapper<GameTencentOrder>()
                .set(GameTencentOrder::getIsBack, backStatus.getBackStatus())
                .set(GameTencentOrder::getBackMoney, backMoney)
                .set(GameTencentOrder::getBackMsg, backMsg)
                .eq(GameTencentOrder::getId, orderLog.getId())
        );
    }


    @Override
    public IPage<GameTencentOrderVO> tencentOrderLogList(GameTencentOrderDTO dto) {
        IPage<GameTencentOrder> page = page(dto.toPage(), new LambdaQueryWrapper<GameTencentOrder>()
                .eq(StringUtils.isNotBlank(dto.getChannel()), GameTencentOrder::getChannel, dto.getChannel())
                .eq(null != dto.getGameId(), GameTencentOrder::getGameId, dto.getGameId())
                .eq(null != dto.getAdAccountId(), GameTencentOrder::getAdAccountId, dto.getAdAccountId())
                .eq(StringUtils.isNotBlank(dto.getWechatAppId()), GameTencentOrder::getWechatAppId, dto.getWechatAppId())
                .eq(StringUtils.isNotBlank(dto.getWechatOpenid()), GameTencentOrder::getWechatOpenid, dto.getWechatOpenid())
                .eq(StringUtils.isNotBlank(dto.getOrderId()), GameTencentOrder::getOrderId, dto.getOrderId())
                .eq(null != dto.getIsBack(), GameTencentOrder::getIsBack, dto.getIsBack())
                .eq(null != dto.getOrderStatus(), GameTencentOrder::getOrderStatus, dto.getOrderStatus())
                .eq(dto.getIsFirstOrder() != null, GameTencentOrder::getIsFirstOrder, dto.getIsFirstOrder())
                .like(StringUtils.isNotBlank(dto.getRoleName()), GameTencentOrder::getRoleName, dto.getRoleName())
                .apply(dto.getRegPayIntervalTimeMin() != null, "if(pay_time is not null, TIMESTAMPDIFF(minute, register_time, pay_time), null) >= {0}", dto.getRegPayIntervalTimeMin())
                .apply(dto.getRegPayIntervalTimeMax() != null, "if(pay_time is not null, TIMESTAMPDIFF(minute, register_time, pay_time), null) <= {0}", dto.getRegPayIntervalTimeMax())
                .orderByDesc(GameTencentOrder::getCreateTime)
        );
        IPage<GameTencentOrderVO> 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<GameTencentOrderVO> toVOBatch(List<GameTencentOrder> logList) {
        if (CollectionUtils.isEmpty(logList)) {
            return Collections.emptyList();
        }
        Set<String> agentKeys = logList.stream().map(GameTencentOrder::getChannel).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 -> {
            GameTencentOrderVO vo = toVOSimple(log);
            if (StringUtils.isNotBlank(log.getChannel())) {
                AgentRpcVO agent = agentMap.get(log.getChannel());
                if (agent != null) {
                    vo.setAgentName(agent.getAgentName());
                }
            }
            return vo;
        }).collect(Collectors.toList());
    }

    private GameTencentOrderVO toVOSimple(GameTencentOrder log) {
        if (log == null) {
            return null;
        }
        GameTencentOrderVO vo = BeanUtil.copy(log, GameTencentOrderVO.class);
        vo.setRegPayIntervalTime(OrderUtil.regPayIntervalTime(log.getPayTime(), log.getRegisterTime()));
        return vo;
    }

    @Override
    public boolean tencentOrderReport(List<Long> ids, Long backMoney) {
        listByIds(ids).stream()
                .filter(order -> !Objects.equals(order.getIsBack(), BackStatusEnum.SUCCESS.getBackStatus()))
                .forEach(orderLog -> orderBack(orderLog, true, backMoney));
        return true;
    }

    private BackStatusEnum doCallback(GameTencentOrder orderLog) {
        String actionType = orderLog.getOrderStatus().equals(OrderStatusEnum.SUCCESS_PAY.getValue()) ?
                ActionTypeEnum.PURCHASE.getActionType() : ActionTypeEnum.COMPLETE_ORDER.getActionType();
        Map<String, Object> actionParam = new HashMap<>(2);
        actionParam.put("claim_type", 0);
        actionParam.put("value", orderLog.getRechargeMoney());
        DataReportOfAppIdRpcDTO dataReportOfAppIdRpcDTO = DataReportOfAppIdRpcDTO.builder()
                .appId(orderLog.getWechatAppId())
                .userActionSetId(orderLog.getUserActionSetId())
                .action(UserActionRpcDTO.builder()
                        .actionTime(orderLog.getOrderStatus().equals(OrderStatusEnum.SUCCESS_PAY.getValue()) ? orderLog.getPayTime() : orderLog.getRechargeTime())
                        .actionType(actionType)
                        .userId(UserActionRpcDTO.UserIdRpcDTO.builder()
                                .wechatAppId(orderLog.getWechatAppId())
                                .wechatOpenid(orderLog.getWechatOpenid())
                                .build())
                        .actionParam(actionParam)
                        .build())
                .build();

        GameTencentBackLog gameTencentBackLog = GameTencentBackLog.builder()
                .gameId(orderLog.getGameId())
                .adAccountId(orderLog.getAdAccountId())
                .actionTime(orderLog.getOrderStatus().equals(OrderStatusEnum.SUCCESS_PAY.getValue()) ? orderLog.getPayTime() : orderLog.getRechargeTime())
                .createTime(LocalDateTime.now())
                .actionType(actionType)
                .orderId(orderLog.getOrderId())
                .wechatAppId(orderLog.getWechatAppId())
                .wechatOpenid(orderLog.getWechatOpenid())
                .userActionSetId(orderLog.getUserActionSetId())
                .actionParam(actionParam.toString())
                .build();
        ResultVO<Boolean> result = userActionSetRpc.reportByAppId(dataReportOfAppIdRpcDTO);
        BackStatusEnum backStatus = BackStatusEnum.FAILED;
        if (result.isSuccess()) {
            backStatus = BackStatusEnum.SUCCESS;
            gameTencentBackLog.setBackLog("回传成功");
        } else {
            log.error("回传腾讯订单失败，失败原因：{}", result.getMsg());
            gameTencentBackLog.setBackLog(result.getMsg());
        }
        gameTencentBackLogService.save(gameTencentBackLog);
        return backStatus;
    }
}
