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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zanxiang.erp.security.util.SecurityUtil;
import com.zanxiang.game.module.base.ServerInfo;
import com.zanxiang.game.module.base.pojo.enums.GameCategoryEnum;
import com.zanxiang.game.module.base.pojo.enums.PayDeviceEnum;
import com.zanxiang.game.module.base.pojo.enums.PayWayEnum;
import com.zanxiang.game.module.base.rpc.IOrderServiceRpc;
import com.zanxiang.game.module.base.util.DateUtils;
import com.zanxiang.game.module.manage.pojo.dto.*;
import com.zanxiang.game.module.manage.pojo.params.OrderParam;
import com.zanxiang.game.module.manage.pojo.params.UserOrderListParam;
import com.zanxiang.game.module.manage.pojo.vo.OrderExcelVO;
import com.zanxiang.game.module.manage.pojo.vo.OrderListVO;
import com.zanxiang.game.module.manage.pojo.vo.OrderVO;
import com.zanxiang.game.module.manage.pojo.vo.UserOrderListVO;
import com.zanxiang.game.module.manage.service.*;
import com.zanxiang.game.module.mybatis.entity.Agent;
import com.zanxiang.game.module.mybatis.entity.GameServer;
import com.zanxiang.game.module.mybatis.entity.Order;
import com.zanxiang.game.module.mybatis.entity.User;
import com.zanxiang.game.module.mybatis.mapper.OrderMapper;
import com.zanxiang.module.util.bean.BeanUtil;
import com.zanxiang.module.util.excel.ExcelUtil;
import com.zanxiang.module.util.exception.BaseException;
import com.zanxiang.module.web.util.WebExcelUtil;
import jakarta.servlet.http.HttpServletResponse;
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 reactor.util.function.Tuple2;

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

/**
 * @author : lingfeng
 * @time : 2022-07-05
 * @description : 用户订单
 */
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private IGameUserRoleService gameUserRoleService;

    @Autowired
    private IGameService gameService;

    @Autowired
    private ICpService cpService;

    @Autowired
    private IUserService userService;

    @Autowired
    private IAgentService agentService;

    @Autowired
    private IPayMerchantService payMerchantService;

    @Autowired
    private IGameServerService gameServerService;

    @Autowired
    private IGameAuthService gameAuthService;

    @DubboReference(providedBy = ServerInfo.SERVER_SDK_DUBBO_NAME)
    private IOrderServiceRpc orderServiceRpc;

    @Override
    public IPage<UserOrderListVO> orderList(UserOrderListParam param) {
        //执行查询
        return page(param.toPage(), new QueryWrapper<Order>().lambda()
                .eq(Order::getUserId, param.getUserId())
                .orderByDesc(Order::getCreateTime)
        ).convert(this::toVo);
    }

    private UserOrderListVO toVo(Order order) {
        if (order == null) {
            return null;
        }
        UserOrderListVO orderListVO = BeanUtil.copy(order, UserOrderListVO.class);
        orderListVO.setId(order.getOrderId());
        //查询角色名称
        GameUserRoleDTO gameUserRoleDTO = gameUserRoleService.getByRoleId(order.getUserId(), order.getGameId(), order.getRoleId());
        if (gameUserRoleDTO != null) {
            orderListVO.setRoleName(gameUserRoleDTO.getRoleName());
        }
        //设置游戏名称
        GameDTO gameDTO = gameService.getById(order.getGameId());
        if (gameDTO == null) {
            return orderListVO;
        }
        //设置游戏列表
        orderListVO.setGameCategoryName(GameCategoryEnum.getNameByCategory(gameDTO.getCategory()));
        orderListVO.setGameName(gameDTO.getName());
        return orderListVO;
    }

    @Override
    public OrderListVO orderList(OrderParam param) {
        //游戏获取
        Tuple2<String, List<Long>> gameTuple = gameAuthService.getUserGameList(param.getPitcherId());
        if (CollectionUtils.isEmpty(gameTuple.getT2())) {
            return new OrderListVO(param.toPage().getSize());
        }
        //渠道获取
        Tuple2<List<Long>, List<AgentDTO>> tuple2 = agentService.getUserAgent(param.getAccountId(), param.getPitcherId(), param.getChannelId());
        List<Long> agentIds = tuple2.getT1();
        //投手没有渠道, 看不见数据
        if (CollectionUtils.isEmpty(agentIds)) {
            return new OrderListVO(param.toPage().getSize());
        }
        //查询用户id和名字条件
        List<Long> userIds = null;
        if (Strings.isBlank(param.getUserId()) && Strings.isNotBlank(param.getUsername())) {
            userIds = userService.list(new LambdaQueryWrapper<User>()
                    .like(User::getUsername, param.getUsername())
            ).stream().map(User::getId).collect(Collectors.toList());
        }
        //区服筛选
        List<String> serverIdList = gameServerService.listServerId(param.getGameId(), param.getServerName());
        //查询订单列表
        IPage<OrderVO> page = page(param.toPage(), getListWrapper(param, gameTuple, agentIds, userIds, serverIdList, "*")).convert(this::toVO);
        if (page.getTotal() == 0) {
            return new OrderListVO(param.toPage().getSize());
        }
        //统计：订单金额与实付金额
        Order total = super.getOne(getListWrapper(param, gameTuple, agentIds, userIds, serverIdList, "IFNULL(SUM(amount),0) amount, IFNULL(SUM(real_amount),0) realAmount"));
        HashMap<String, BigDecimal> totalData = new HashMap<>(2);
        if (total == null) {
            totalData.put("totalOrderAmount", new BigDecimal("0.00"));
            totalData.put("totalRealAmount", new BigDecimal("0.00"));
        } else {
            totalData.put("totalOrderAmount", total.getAmount());
            totalData.put("totalRealAmount", total.getRealAmount());
        }
        //分页数据
        List<OrderVO> orderListVOList = page.getRecords();
        //cp信息
        Map<Long, CpDTO> cpMap = cpService.cpMap();
        //玩家信息
        List<Long> userIdList = orderListVOList.stream().map(OrderVO::getUserId).collect(Collectors.toList());
        Map<Long, UserDTO> userMap = userService.getByUserIds(userIdList);
        //渠道
        Map<Long, AgentDTO> agentMap = tuple2.getT2().stream().collect(Collectors.toMap(AgentDTO::getId, Function.identity()));
        //游戏信息
        Map<Long, GameDTO> gameMap = gameService.gameMap(orderListVOList.stream()
                .map(OrderVO::getGameId).collect(Collectors.toSet()));
        //商户列表
        Map<String, PayMerchantDTO> payMerchantMap = payMerchantService.payMerchantMap();
        //字段信息补充更新
        orderListVOList.forEach(vo -> this.toVO(vo, userMap, gameMap, cpMap, agentMap, payMerchantMap));
        //设置返回
        OrderListVO orderListVO = new OrderListVO();
        orderListVO.setSize(page.getSize());
        orderListVO.setTotal(page.getTotal());
        orderListVO.setCurrent(page.getCurrent());
        orderListVO.setPages(page.getPages());
        orderListVO.setData(page.getRecords());
        orderListVO.setTotalData(totalData);
        return orderListVO;
    }

    private OrderVO toVO(Order order) {
        if (Objects.isNull(order)) {
            return null;
        }
        OrderVO orderVO = BeanUtil.copy(order, OrderVO.class);
        //原始区服名称
        GameDTO gameDTO = gameService.getById(order.getGameId());
        GameServer gameServer = gameServerService.getOne(new LambdaQueryWrapper<GameServer>()
                .eq(GameServer::getGameId, gameDTO.getSuperGameId())
                .eq(GameServer::getServerId, order.getServerId()));
        if (gameServer != null) {
            orderVO.setSourceServerName(gameServer.getServerName());
        }
        //支付渠道, 场景
        orderVO.setPayWayName(PayWayEnum.getNameByPayWayId(orderVO.getPayWayId()));
        orderVO.setPayDeviceName(PayDeviceEnum.getByPayWayId(orderVO.getPayDeviceId()));
        //注册充值时间差
        orderVO.setRegPayTimeDiff(DateUtils.getRegPayTimeDiff(orderVO.getRegTime(), orderVO.getPayTime()));
        //返回
        return orderVO;
    }

    private void toVO(OrderVO orderVO, Map<Long, UserDTO> userMap, Map<Long, GameDTO> gameMap, Map<Long, CpDTO> cpMap,
                      Map<Long, AgentDTO> agentMap, Map<String, PayMerchantDTO> payMerchantMap) {
        orderVO.setDateTime(orderVO.getCreateTime().toLocalDate());
        //用户信息补充
        UserDTO userDTO = userMap.get(orderVO.getUserId());
        if (userDTO != null) {
            orderVO.setUsername(userDTO.getUsername());
            if (orderVO.getPayTime() != null) {
                orderVO.setRegPayTimeDiff(Duration.between(userDTO.getCreateTime(), orderVO.getPayTime()).getSeconds());
            }
        }
        //渠道账号
        AgentDTO agentDTO = agentMap.containsKey(orderVO.getAgentId()) ? agentMap.get(orderVO.getAgentId()) : agentService.getAgentById(orderVO.getAgentId());
        if (agentDTO != null) {
            orderVO.setAccountId(agentDTO.getAccountId());
            orderVO.setPitcherId(agentDTO.getCreateBy());
            orderVO.setPitcherName(agentDTO.getCreateByName());
            orderVO.setAccountType(agentDTO.getAccountType());
            orderVO.setAgentName(agentDTO.getAgentName());
        }
        if (Objects.equals(orderVO.getAgentId(), Agent.DEFAULT_AGENT)) {
            orderVO.setAgentName(Agent.DEFAULT_AGENT_NAME);
        }
        //CP名称
        CpDTO cpDTO = cpMap.get(orderVO.getCpId());
        orderVO.setCpName(cpDTO == null ? null : cpDTO.getCpName());
        //商户名称
        PayMerchantDTO payMerchantDTO = payMerchantMap.get(orderVO.getMerchantNo());
        orderVO.setMerchantName(payMerchantDTO == null ? null : payMerchantDTO.getMerchantName());
        //游戏信息
        GameDTO gameDTO = gameMap.get(orderVO.getGameId());
        if (gameDTO != null) {
            orderVO.setGameName(gameDTO.getName());
            GameCategoryEnum gameCategoryEnum = GameCategoryEnum.getGameCategory(gameDTO.getCategory());
            orderVO.setGameCategoryId(gameCategoryEnum == null ? null : gameCategoryEnum.getId());
            orderVO.setGameCategoryName(gameCategoryEnum == null ? null : gameCategoryEnum.getName());
        }
    }

    private LambdaQueryWrapper<Order> getListWrapper(OrderParam param, Tuple2<String, List<Long>> gameTuple, List<Long> agentIdList,
                                                     List<Long> userIdList, List<String> serverIdList, String select) {
        return new QueryWrapper<Order>().select(select).lambda()
                .in(!SecurityUtil.isAdmin(), Order::getGameId, gameTuple.getT2())
                .eq(Strings.isNotBlank(param.getOrderId()), Order::getOrderId, param.getOrderId())
                .eq(Strings.isNotBlank(param.getCpOrderId()), Order::getCpOrderId, param.getCpOrderId())
                .eq(Strings.isNotBlank(param.getMerchantOrderNo()), Order::getMerchantOrderNo, param.getMerchantOrderNo())
                .eq(Strings.isNotBlank(param.getUserId()), Order::getUserId, param.getUserId())
                .in(CollectionUtils.isNotEmpty(userIdList), Order::getUserId, userIdList)
                .in(CollectionUtils.isNotEmpty(serverIdList), Order::getServerId, serverIdList)
                .like(CollectionUtils.isEmpty(serverIdList) && Strings.isNotBlank(param.getServerName()), Order::getServerName, param.getServerName())
                .like(Strings.isNotBlank(param.getRoleName()), Order::getRoleName, param.getRoleName())
                .eq(param.getCpId() != null, Order::getCpId, param.getCpId())
                .eq(param.getGameId() != null, Order::getGameId, param.getGameId())
                .in(CollectionUtils.isNotEmpty(agentIdList), Order::getAgentId, agentIdList)
                .eq(Strings.isNotBlank(param.getDeviceSystem()), Order::getDeviceSystem, param.getDeviceSystem())
                .eq(param.getIsSwitch() != null, Order::getIsSwitch, param.getIsSwitch())
                .eq(Strings.isNotBlank(param.getProductName()), Order::getProductName, param.getProductName())
                .eq(Strings.isNotBlank(param.getPayWayId()), Order::getPayWayId, param.getPayWayId())
                .eq(Strings.isNotBlank(param.getMerchantNo()), Order::getMerchantNo, param.getMerchantNo())
                .eq(param.getStatus() != null, Order::getStatus, param.getStatus())
                .eq(param.getCpStatus() != null, Order::getCpStatus, param.getCpStatus())
                .eq(param.getIsFirstRecharge() != null, Order::getIsFirstRecharge, param.getIsFirstRecharge())
                .eq(param.getCallBackStatus() != null, Order::getCallBackStatus, param.getCallBackStatus())
                .ge(param.getBeginDate() != null, Order::getCreateTime, param.getBeginDate() == null ? null : LocalDateTime.of(param.getBeginDate(), LocalTime.MIN))
                .le(param.getEndDate() != null, Order::getCreateTime, param.getEndDate() == null ? null : LocalDateTime.of(param.getEndDate(), LocalTime.MAX))
                .ge(param.getRegTimeStart() != null, Order::getRegTime, param.getRegTimeStart() == null ? null : LocalDateTime.of(param.getRegTimeStart(), LocalTime.MIN))
                .le(param.getRegTimeEnd() != null, Order::getRegTime, param.getRegTimeEnd() == null ? null : LocalDateTime.of(param.getRegTimeEnd(), LocalTime.MAX))
                .ge(param.getPayTimeStart() != null, Order::getPayTime, param.getPayTimeStart() == null ? null : LocalDateTime.of(param.getPayTimeStart(), LocalTime.MIN))
                .le(param.getPayTimeEnd() != null, Order::getPayTime, param.getPayTimeEnd() == null ? null : LocalDateTime.of(param.getPayTimeEnd(), LocalTime.MAX))
                .apply(param.getRegPayIntervalTimeMin() != null, "if(pay_time is not null, TIMESTAMPDIFF(minute, reg_time, pay_time), null) >= {0}", param.getRegPayIntervalTimeMin())
                .apply(param.getRegPayIntervalTimeMax() != null, "if(pay_time is not null, TIMESTAMPDIFF(minute, reg_time, pay_time), null) <= {0}", param.getRegPayIntervalTimeMax())
                .orderByDesc(Order::getCreateTime);
    }

    @Override
    public void getOrderExcel(OrderParam param, HttpServletResponse response) {
        if (!SecurityUtil.isAdmin()) {
            throw new BaseException("没有下载权限");
        }
        //游戏获取
        Tuple2<String, List<Long>> gameTuple = gameAuthService.getUserGameList(param.getPitcherId());
        List<Long> gameIdList = gameTuple.getT2();
        //渠道获取
        Tuple2<List<Long>, List<AgentDTO>> tuple2 = agentService.getUserAgent(param.getAccountId(), param.getPitcherId(), param.getChannelId());
        List<Long> agentIds = tuple2.getT1();
        //未获取到游戏或者渠道列表
        if (CollectionUtils.isEmpty(gameIdList) || CollectionUtils.isEmpty(agentIds)) {
            return;
        }
        //渠道
        Map<Long, AgentDTO> agentMap = tuple2.getT2().stream().collect(Collectors.toMap(AgentDTO::getId, Function.identity()));
        //查询用户id和名字条件
        List<User> userList = new ArrayList<>();
        if (Strings.isBlank(param.getUserId()) && Strings.isNotBlank(param.getUsername())) {
            userList = userService.list(new LambdaQueryWrapper<User>()
                    .like(User::getUsername, param.getUsername()));
        }
        //商户列表
        Map<String, PayMerchantDTO> payMerchantMap = payMerchantService.payMerchantMap();
        //cp信息
        Map<Long, CpDTO> cpMap = cpService.cpMap();
        final List<Long> userIds = userList.stream().map(User::getId).collect(Collectors.toList());
        List<String> serverIdList = gameServerService.listServerId(param.getGameId(), param.getServerName());
        //excel下载
        WebExcelUtil.httpExport(response, "游戏订单", OrderExcelVO.class, ((pageNumber, pageSize) -> {
            //查询订单
            List<OrderVO> orderVOList = page(new Page<>(pageNumber, pageSize), this.getListWrapper(param, gameTuple,
                    agentIds, userIds, serverIdList, "*")).convert(this::toVO).getRecords();
            //玩家信息
            List<Long> userIdList = orderVOList.stream().map(OrderVO::getUserId).collect(Collectors.toList());
            Map<Long, UserDTO> userMap = userService.getByUserIds(userIdList);
            //游戏信息
            Map<Long, GameDTO> gameMap = gameService.gameMap(orderVOList.stream().map(OrderVO::getGameId).collect(Collectors.toSet()));
            //字段信息补充更新
            orderVOList.forEach(vo -> this.toVO(vo, userMap, gameMap, cpMap, agentMap, payMerchantMap));
            //excel字段赋值
            return orderVOList.stream().map(orderVO -> BeanUtil.copy(orderVO, OrderExcelVO.class)).collect(Collectors.toList());
        }), ExcelUtil.DEFAULT_MAX_DATA_SIZE);
    }

    @Override
    public Boolean callCp(String orderId, String payKey) {
        return orderServiceRpc.orderCpCall(orderId, payKey);
    }
}
