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.manage.constant.RedisKeyConstant;
import com.zanxiang.game.module.manage.pojo.params.GameGiftPackCodeAddParam;
import com.zanxiang.game.module.manage.pojo.params.GameGiftPackCodeListParam;
import com.zanxiang.game.module.manage.pojo.params.GameGiftPackCodeUpdateParam;
import com.zanxiang.game.module.manage.pojo.vo.GameGiftPackCodeVO;
import com.zanxiang.game.module.manage.service.IGameGiftPackCodeService;
import com.zanxiang.game.module.manage.service.IGameGiftPackLinkService;
import com.zanxiang.game.module.manage.utils.RedisUtil;
import com.zanxiang.game.module.mybatis.entity.GameGiftPackCode;
import com.zanxiang.game.module.mybatis.entity.GameGiftPackLink;
import com.zanxiang.game.module.mybatis.mapper.GameGiftPackCodeMapper;
import com.zanxiang.module.util.bean.BeanUtil;
import com.zanxiang.module.util.exception.BaseException;
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.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author : lingfeng
 * @time : 2022-06-22
 * @description : 游戏礼包码
 */
@Slf4j
@Service
public class GameGiftPackCodeServiceImpl extends ServiceImpl<GameGiftPackCodeMapper, GameGiftPackCode> implements IGameGiftPackCodeService {

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

    @Autowired
    private IGameGiftPackLinkService gameGiftPackLinkService;

    @Autowired
    private RedisUtil<String> redisUtil;

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

    private List<GameGiftPackCodeVO> toVOBatch(List<GameGiftPackCode> codeList) {
        //用户信息
        List<Long> userIdList = codeList.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 codeList.stream().map(code -> {
            GameGiftPackCodeVO vo = BeanUtil.copy(code, GameGiftPackCodeVO.class);
            vo.setCreateByName(userMap.get(code.getCreateBy()));
            vo.setUpdateByName(userMap.get(code.getUpdateBy()));
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addGiftPackCode(GameGiftPackCodeAddParam param) {
        //查询礼包码链接信息
        GameGiftPackLink gameGiftPackLink = gameGiftPackLinkService.getOne(new LambdaQueryWrapper<GameGiftPackLink>()
                .eq(GameGiftPackLink::getId, param.getLinkId())
                .eq(GameGiftPackLink::getGameId, param.getGameId())
        );
        //礼包码链接信息不可为空
        Assert.notNull(gameGiftPackLink, "参数错误, 礼包码链接信息不存在");
        //判断已存在的礼包码
        List<String> existCodeList = super.list(new LambdaQueryWrapper<GameGiftPackCode>()
                .select(GameGiftPackCode::getCode)
                .eq(GameGiftPackCode::getLinkId, param.getLinkId())
                .eq(GameGiftPackCode::getEnabled, Boolean.TRUE)
                .eq(GameGiftPackCode::getGameId, param.getGameId())
                .in(GameGiftPackCode::getCode, param.getCodeList())
        ).stream().map(GameGiftPackCode::getCode).collect(Collectors.toList());
        //构造新的礼包码
        List<GameGiftPackCode> saveList = param.getCodeList().stream()
                .filter(code -> !existCodeList.contains(code))
                .map(code -> this.transform(param, code))
                .collect(Collectors.toList());
        Assert.state(CollectionUtils.isNotEmpty(saveList), "参数错误, 礼包码已存在, 禁止重复添加");
        //保存返回
        super.saveBatch(saveList);
        //将礼包码主键添加到缓存
        String[] codeIdArray = saveList.stream().map(GameGiftPackCode::getId).map(Object::toString).toArray(String[]::new);
        if (redisUtil.addToSet(RedisKeyConstant.GAME_GIFT_PACK_CODE + param.getLinkId(), codeIdArray)) {
            return true;
        }
        throw new BaseException("添加礼包码异常, 添加到缓存失败列表");
    }

    private GameGiftPackCode transform(GameGiftPackCodeAddParam param, String code) {
        return GameGiftPackCode.builder()
                .linkId(param.getLinkId())
                .gameId(param.getGameId())
                .code(code)
                .enabled(Boolean.TRUE)
                .isSend(Boolean.FALSE)
                .createBy(SecurityUtil.getUserId())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
    }

    @Override
    public boolean updateGiftPackCode(GameGiftPackCodeUpdateParam param) {
        String giftPackCodeKey = RedisKeyConstant.GAME_GIFT_PACK_CODE + param.getLinkId();
        List<Long> giftPackCodeIdList = param.getIds().stream()
                .filter(id -> redisUtil.isMemberInSet(giftPackCodeKey, String.valueOf(id)))
                .collect(Collectors.toList());
        //不存在缓存队列中, 不可以删除
        Assert.notEmpty(giftPackCodeIdList, "缓存队列中不存在可删除的礼包码");
        //先从缓存中移除
        redisUtil.removeOfSet(giftPackCodeKey, giftPackCodeIdList.stream().map(String::valueOf).toArray(String[]::new));
        //更新数据库
        return super.update(new LambdaUpdateWrapper<GameGiftPackCode>()
                .set(GameGiftPackCode::getEnabled, Boolean.FALSE)
                .set(GameGiftPackCode::getUpdateBy, SecurityUtil.getUserId())
                .set(GameGiftPackCode::getUpdateTime, LocalDateTime.now())
                .in(GameGiftPackCode::getId, giftPackCodeIdList)
        );
    }
}