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.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.zanxiang.erp.base.ErpServer;
import com.zanxiang.erp.base.rpc.ISysUserRpc;
import com.zanxiang.erp.security.util.SecurityUtil;
import com.zanxiang.game.module.base.pojo.enums.DeleteEnum;
import com.zanxiang.game.module.manage.constant.RedisKeyConstant;
import com.zanxiang.game.module.manage.pojo.dto.GameDTO;
import com.zanxiang.game.module.manage.pojo.dto.GameGiftPackConditionDTO;
import com.zanxiang.game.module.manage.pojo.params.GameGiftPackLinkAddParam;
import com.zanxiang.game.module.manage.pojo.params.GameGiftPackLinkListParam;
import com.zanxiang.game.module.manage.pojo.params.GameGiftPackLinkUpdateParam;
import com.zanxiang.game.module.manage.pojo.vo.GameGiftPackLinkVO;
import com.zanxiang.game.module.manage.service.IGameGiftPackCodeService;
import com.zanxiang.game.module.manage.service.IGameGiftPackConfigService;
import com.zanxiang.game.module.manage.service.IGameGiftPackLinkService;
import com.zanxiang.game.module.manage.service.IGameService;
import com.zanxiang.game.module.manage.utils.RedisUtil;
import com.zanxiang.game.module.mybatis.entity.Game;
import com.zanxiang.game.module.mybatis.entity.GameGiftPackCode;
import com.zanxiang.game.module.mybatis.entity.GameGiftPackConfig;
import com.zanxiang.game.module.mybatis.entity.GameGiftPackLink;
import com.zanxiang.game.module.mybatis.mapper.GameGiftPackLinkMapper;
import com.zanxiang.module.util.JsonUtil;
import com.zanxiang.module.util.bean.BeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
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 org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author : lingfeng
 * @time : 2022-06-22
 * @description : 游戏礼包码链接
 */
@Slf4j
@Service
public class GameGiftPackLinkServiceImpl extends ServiceImpl<GameGiftPackLinkMapper, GameGiftPackLink> implements IGameGiftPackLinkService {

    @DubboReference(providedBy = ErpServer.SERVER_DUBBO_NAME)
    private ISysUserRpc sysUserRpc;

    @Autowired
    private IGameService gameService;

    @Autowired
    private RedisUtil<String> redisUtil;

    @Autowired
    private IGameGiftPackCodeService gameGiftPackCodeService;

    @Autowired
    private IGameGiftPackConfigService gameGiftPackConfigService;

    @Override
    public IPage<GameGiftPackLinkVO> list(GameGiftPackLinkListParam param) {
        IPage<GameGiftPackLink> giftPackLinkIPage = page(param.toPage(), new QueryWrapper<GameGiftPackLink>().lambda()
                .eq(param.getGameId() != null, GameGiftPackLink::getGameId, param.getGameId())
                .eq(Strings.isNotBlank(param.getCodeType()), GameGiftPackLink::getCodeType, param.getCodeType())
                .eq(Strings.isNotBlank(param.getCodeLink()), GameGiftPackLink::getCodeLink, param.getCodeLink())
                .ge(param.getCreateBeginTime() != null, GameGiftPackLink::getCreateTime, param.getCreateBeginTime() == null ? null : LocalDateTime.of(param.getCreateBeginTime(), LocalTime.MIN))
                .le(param.getCreateEndTime() != null, GameGiftPackLink::getCreateTime, param.getCreateEndTime() == null ? null : LocalDateTime.of(param.getCreateEndTime(), LocalTime.MAX))
                .ge(param.getUpdateBeginTime() != null, GameGiftPackLink::getUpdateTime, param.getUpdateBeginTime() == null ? null : LocalDateTime.of(param.getUpdateBeginTime(), LocalTime.MIN))
                .le(param.getUpdateEndTime() != null, GameGiftPackLink::getUpdateTime, param.getUpdateEndTime() == null ? null : LocalDateTime.of(param.getUpdateEndTime(), LocalTime.MAX))
                .orderByDesc(GameGiftPackLink::getUpdateTime));
        IPage<GameGiftPackLinkVO> result = new Page<>(giftPackLinkIPage.getCurrent(), giftPackLinkIPage.getSize(), giftPackLinkIPage.getTotal());
        if (CollectionUtils.isNotEmpty(giftPackLinkIPage.getRecords())) {
            result.setRecords(this.toVOBatch(giftPackLinkIPage.getRecords()));
        }
        return result;
    }

    private List<GameGiftPackLinkVO> toVOBatch(List<GameGiftPackLink> linkList) {
        //游戏信息
        List<Long> gameIdList = linkList.stream().map(GameGiftPackLink::getGameId).collect(Collectors.toList());
        Map<Long, Game> gameMap = gameService.listByIds(gameIdList).stream()
                .collect(Collectors.toMap(Game::getId, Function.identity()));
        //查询配置
        List<Long> supperGameIdList = gameMap.values().stream().map(Game::getSuperGameId).collect(Collectors.toList());
        Map<Long, GameGiftPackConfig> configMap = gameGiftPackConfigService.list(new LambdaQueryWrapper<GameGiftPackConfig>()
                .in(GameGiftPackConfig::getSuperGameId, supperGameIdList)
        ).stream().collect(Collectors.toMap(GameGiftPackConfig::getId, Function.identity()));
        //用户信息
        List<Long> userIdList = linkList.stream()
                .flatMap(link -> Stream.of(link.getCreateBy(), link.getUpdateBy()))
                .filter(Objects::nonNull)
                .distinct().collect(Collectors.toList());
        Map<Long, String> userMap = sysUserRpc.getUserNameByIds(new ArrayList<>(userIdList)).getData();
        //循环构造
        return linkList.stream().map(link -> {
            GameGiftPackLinkVO vo = BeanUtil.copy(link, GameGiftPackLinkVO.class);
            vo.setConditionDTO(JsonUtil.toObj(link.getCodeCheck(), GameGiftPackConditionDTO.class));
            vo.setCreateByName(userMap.get(link.getCreateBy()));
            vo.setUpdateByName(userMap.get(link.getUpdateBy()));
            Game game = gameMap.get(link.getGameId());
            if (game != null) {
                vo.setGameName(game.getName());
            }
            GameGiftPackConfig gameGiftPackConfig = configMap.get(link.getConfigId());
            if (gameGiftPackConfig != null) {
                vo.setCodeTypeName(this.codeTypeName(link.getCodeType(), gameGiftPackConfig.getCodeTypeConfig()));
            }
            return vo;
        }).collect(Collectors.toList());
    }

    private String codeTypeName(String codeType, String codeTypeConfig) {
        if (Strings.isBlank(codeTypeConfig) || Strings.isBlank(codeType)) {
            return null;
        }
        Map<String, String> codeTypeMap = JsonUtil.toMap(codeTypeConfig, Map.class, String.class);
        return codeTypeMap.get(codeType);
    }

    @Override
    public boolean updateGiftPackLink(GameGiftPackLinkUpdateParam param) {
        return super.update(new LambdaUpdateWrapper<GameGiftPackLink>()
                .set(GameGiftPackLink::getCodeType, param.getCodeType())
                .set(GameGiftPackLink::getCodeCheck, param.getConditionDTO() == null ? null : JsonUtil.toString(param.getConditionDTO()))
                .set(GameGiftPackLink::getUpdateBy, SecurityUtil.getUserId())
                .set(GameGiftPackLink::getUpdateTime, LocalDateTime.now())
                .eq(GameGiftPackLink::getId, param.getId())
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(Long id) {
        //链接里存在可用的未被领取的礼包码, 禁止删除
        Assert.state(gameGiftPackCodeService.count(new LambdaUpdateWrapper<GameGiftPackCode>()
                .eq(GameGiftPackCode::getLinkId, id)
                .eq(GameGiftPackCode::getEnabled, Boolean.TRUE)
                .eq(GameGiftPackCode::getIsSend, Boolean.FALSE)
        ) <= 0, "礼包码链接中存在未被领取的礼包码, 禁止删除");
        //删除链接
        super.update(new LambdaUpdateWrapper<GameGiftPackLink>()
                .set(GameGiftPackLink::getIsDelete, DeleteEnum.YES.getCode())
                .set(GameGiftPackLink::getUpdateBy, SecurityUtil.getUserId())
                .set(GameGiftPackLink::getUpdateTime, LocalDateTime.now())
                .eq(GameGiftPackLink::getId, id)
        );
        //删除缓存
        return redisUtil.deleteCache(RedisKeyConstant.GAME_GIFT_PACK_CODE + id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addGiftPackLink(GameGiftPackLinkAddParam param) {
        //判断该游戏该类型是否已经存在礼包链接
        Assert.state(super.count(new LambdaQueryWrapper<GameGiftPackLink>()
                .eq(GameGiftPackLink::getGameId, param.getGameId())
                .eq(GameGiftPackLink::getCodeType, param.getCodeType())
        ) <= 0, "该游戏已经存在此类型礼包码链接, 请勿重复添加");
        //查询超父游戏id
        GameDTO gameDTO = gameService.getById(param.getGameId());
        //游戏不可以为空
        Assert.notNull(gameDTO, "参数错误, 游戏信息不存在");
        //查询配置
        GameGiftPackConfig giftPackConfig = gameGiftPackConfigService.getOne(new LambdaQueryWrapper<GameGiftPackConfig>()
                .eq(GameGiftPackConfig::getSuperGameId, gameDTO.getSuperGameId()));
        //礼包码配置不可为空
        Assert.state(giftPackConfig.getGameIdList().contains(param.getGameId()), "参数错误, 游戏礼包码配置不存在");
        //链接域名必须存在
        Assert.state(Strings.isNotBlank(giftPackConfig.getCodeLinkHost()), "数据错误, 配置链接域名不可为空");
        //礼包码链接
        String codeLink = giftPackConfig.getCodeLinkHost() + "?gameId=" + param.getGameId() + "&codeType=" + param.getCodeType();
        //保存且返回结果
        return super.save(this.transform(giftPackConfig.getId(), codeLink, param));
    }

    private GameGiftPackLink transform(Long configId, String codeLink, GameGiftPackLinkAddParam param) {
        return GameGiftPackLink.builder()
                .configId(configId)
                .gameId(param.getGameId())
                .codeType(param.getCodeType())
                .codeCheck(JsonUtil.toString(param.getConditionDTO()))
                .codeLink(codeLink)
                .createBy(SecurityUtil.getUserId())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
    }
}