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.core.toolkit.CollectionUtils;
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.enums.GameServerAssignEnum;
import com.zanxiang.game.module.manage.enums.GameServerExcelEnum;
import com.zanxiang.game.module.manage.pojo.dto.GameDTO;
import com.zanxiang.game.module.manage.pojo.dto.GameServerExcelDTO;
import com.zanxiang.game.module.manage.pojo.params.GameMergeServerParam;
import com.zanxiang.game.module.manage.pojo.params.GameServerAddUpdateParam;
import com.zanxiang.game.module.manage.pojo.params.GameServerAssignParam;
import com.zanxiang.game.module.manage.pojo.params.GameServerListParam;
import com.zanxiang.game.module.manage.pojo.vo.GameServerExcelVO;
import com.zanxiang.game.module.manage.pojo.vo.GameServerListVO;
import com.zanxiang.game.module.manage.pojo.vo.GameServerVO;
import com.zanxiang.game.module.manage.service.*;
import com.zanxiang.game.module.mybatis.entity.GameServer;
import com.zanxiang.game.module.mybatis.entity.GameServerAssignLog;
import com.zanxiang.game.module.mybatis.entity.GameSupper;
import com.zanxiang.game.module.mybatis.mapper.GameServerMapper;
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 org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import reactor.util.function.Tuple2;

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

/**
 * @author : lingfeng
 * @time : 2023-08-07
 * @description : 游戏区服
 */
@Slf4j
@Service
public class GameServerServiceImpl extends ServiceImpl<GameServerMapper, GameServer> implements IGameServerService {

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

    @Autowired
    private IGameService gameService;

    @Autowired
    private IGameAuthService gameAuthService;

    @Autowired
    private IGameSupperService gameSupperService;

    @Autowired
    private IGameServerAssignLogService gameServerAssignLogService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean gameServerAssignBatch(GameServerAssignParam param) {
        List<GameServer> gameServerList = super.listByIds(param.getIdList());
        if (CollectionUtils.isEmpty(gameServerList)) {
            throw new BaseException("参数错误, 区服信息不存在");
        }
        gameServerList.forEach(gameServer -> this.gameServerAssign(param, gameServer));
        return Boolean.TRUE;
    }

    private void gameServerAssign(GameServerAssignParam param, GameServer gameServer) {
//        if (Objects.equals(gameServer.getIsSourceServer(), Boolean.FALSE)) {
//            return;
//        }
        //老的指派人员id
        String oldUserIds = null;
        if (Objects.equals(param.getAssignType(), GameServerAssignEnum.GAME_SERVER_ASSIGN_GS)) {
            oldUserIds = gameServer.getGsIds();
        }
        if (Objects.equals(param.getAssignType(), GameServerAssignEnum.GAME_SERVER_ASSIGN_CUSTOMER)) {
            oldUserIds = gameServer.getCustomerIds();
        }
        //新的指派人员id
        String userIds = null;
        if (CollectionUtils.isNotEmpty(param.getAssignUserIdList())) {
            List<String> collect = param.getAssignUserIdList().stream()
                    .map(String::valueOf).collect(Collectors.toList());
            userIds = String.join(",", collect);
        }
        //更新区服信息
        super.update(new LambdaUpdateWrapper<GameServer>()
                .eq(GameServer::getId, gameServer.getId())
                .set(Objects.equals(param.getAssignType(), GameServerAssignEnum.GAME_SERVER_ASSIGN_GS),
                        GameServer::getGsIds, userIds)
                .set(Objects.equals(param.getAssignType(), GameServerAssignEnum.GAME_SERVER_ASSIGN_CUSTOMER),
                        GameServer::getCustomerIds, userIds));
        //新增保存指派记录
        gameServerAssignLogService.save(GameServerAssignLog.builder()
                .gameId(gameServer.getGameId())
                .serverId(gameServer.getServerId())
                .assignType(param.getAssignType().getValue())
                .oldUserIds(oldUserIds)
                .newUserIds(userIds)
                .createBy(SecurityUtil.getUserId())
                .createTime(LocalDateTime.now())
                .build());
    }

    @Override
    public Boolean gameServerExcelImport(MultipartFile multipartFile, GameServerExcelEnum gameServerExcelEnum, Long gameId) {
        StringBuilder stringBuilder = new StringBuilder();
        List<GameServerExcelDTO> gameServerExcelList;
        //文件流转换
        try {
            gameServerExcelList = ExcelUtil.importFromStream(multipartFile.getInputStream(), GameServerExcelDTO.class, stringBuilder);
        } catch (IOException e) {
            throw new BaseException(stringBuilder.toString());
        }
        //原始区服添加
        if (Objects.equals(gameServerExcelEnum, GameServerExcelEnum.SERVER_ADD)) {
            List<GameServer> gameServerList = new ArrayList<>();
            gameServerExcelList.forEach(excelDTO -> {
                //判断单游戏内区服id不可重复
                if (super.count(new LambdaQueryWrapper<GameServer>()
                        .eq(GameServer::getGameId, gameId)
                        .eq(GameServer::getServerId, excelDTO.getServerId())
                ) > 0) {
                    throw new BaseException("该游戏区服id : " + excelDTO.getServerId() + "已存在, 禁止重复添加");
                }
                gameServerList.add(this.transform(excelDTO, gameId));
            });
            return super.saveBatch(gameServerList);
        }
        //合服添加
        List<GameServer> gameServerList = new ArrayList<>();
        List<String> mergeSerIdList = new ArrayList<>();
        gameServerExcelList.forEach(excelDTO -> {
            Set<String> sonServerIdList = Arrays.stream(excelDTO.getServerIds().split(",")).collect(Collectors.toSet());
            List<GameServer> sonServerList = super.list(new LambdaQueryWrapper<GameServer>()
                    .eq(GameServer::getGameId, gameId)
                    .eq(GameServer::getIsMerge, Boolean.FALSE)
                    .in(GameServer::getServerId, sonServerIdList));
            if (CollectionUtils.isEmpty(sonServerList)) {
                throw new BaseException("合服子服列表查询为空, 请检查子服列表是否存在错误id, 或者是否包含已参与过合服的区服");
            }
            if (!sonServerIdList.contains(excelDTO.getMainServerId())) {
                throw new BaseException("合服主服id : " + excelDTO.getMainServerId() + "不存在子服id列表中, 请检查数据");
            }
            if (sonServerIdList.size() != sonServerList.size()) {
                List<String> collect = sonServerList.stream().map(GameServer::getServerId).collect(Collectors.toList());
                List<String> list = sonServerIdList.stream().filter(son -> !collect.contains(son)).collect(Collectors.toList());
                throw new BaseException("提交的子服中存在错误的区服id : " + list.toString());
            }
            Set<String> sourceServerIds = new HashSet<>();
            sonServerList.forEach(gameServer -> {
                if (gameServer.getIsSourceServer()) {
                    sourceServerIds.add(gameServer.getServerId());
                    return;
                }
                if (Strings.isNotBlank(gameServer.getSourceServerIds())) {
                    Collections.addAll(sourceServerIds, gameServer.getSourceServerIds().split(","));
                }
            });
            gameServerList.add(this.transform(excelDTO, sonServerIdList, sourceServerIds, gameId));
            mergeSerIdList.addAll(sonServerIdList);
        });
        super.saveBatch(gameServerList);
        return super.update(new LambdaUpdateWrapper<GameServer>()
                .set(GameServer::getIsMerge, Boolean.TRUE)
                .set(GameServer::getUpdateTime, LocalDateTime.now())
                .eq(GameServer::getGameId, gameId)
                .in(GameServer::getServerId, mergeSerIdList));
    }

    private GameServer transform(GameServerExcelDTO excelDTO, Long gameId) {
        return GameServer.builder()
                .gameId(gameId)
                .serverId(excelDTO.getServerId())
                .serverName(excelDTO.getServerName())
                .nickName(excelDTO.getNickName())
                .startTime(excelDTO.getStartTime())
                .isDelete(DeleteEnum.NO.getCode())
                .createBy(SecurityUtil.getUserId())
                .createTime(LocalDateTime.now())
                .isSourceServer(Boolean.TRUE)
                .isMerge(Boolean.FALSE)
                .updateBy(SecurityUtil.getUserId())
                .updateTime(LocalDateTime.now())
                .build();
    }

    private GameServer transform(GameServerExcelDTO excelDTO, Set<String> sonServerIds, Set<String> sourceServerIds, Long gameId) {
        return GameServer.builder()
                .gameId(gameId)
                .serverId(UUID.randomUUID().toString())
                .serverName(excelDTO.getServerName())
                .isSourceServer(Boolean.FALSE)
                .mainServerId(excelDTO.getMainServerId())
                .isMerge(Boolean.FALSE)
                .sonServerIds(String.join(",", sonServerIds))
                .sourceServerIds(String.join(",", sourceServerIds))
                .mergeTime(excelDTO.getMergeTime())
                .startTime(excelDTO.getMergeTime())
                .isDelete(DeleteEnum.NO.getCode())
                .createBy(SecurityUtil.getUserId())
                .createTime(LocalDateTime.now())
                .updateBy(SecurityUtil.getUserId())
                .updateTime(LocalDateTime.now())
                .build();
    }

    @Override
    public List<String> listServerId(Long gameId, String serverName) {
        if (Strings.isBlank(serverName)) {
            return Collections.emptyList();
        }
        GameDTO gameDTO = null;
        if (gameId != null) {
            gameDTO = gameService.getById(gameId);
        }
        return super.list(new LambdaQueryWrapper<GameServer>()
                .eq(gameDTO != null, GameServer::getGameId, gameDTO == null ? null : gameDTO.getSuperGameId())
                .like(Strings.isNotBlank(serverName), GameServer::getServerName, serverName)
        ).stream().map(GameServer::getServerId).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addOrUpdate(GameServerAddUpdateParam param) {
        //判断单游戏内区服id不可重复
        if (super.count(new LambdaQueryWrapper<GameServer>()
                .eq(GameServer::getGameId, param.getGameId())
                .eq(GameServer::getServerId, param.getServerId())
                .ne(param.getId() != null, GameServer::getId, param.getId())
        ) > 0) {
            throw new BaseException("该游戏区服id已存在, 禁止添加或者修改");
        }
        //添加或者更新
        return super.saveOrUpdate(this.transform(param));
    }

    private GameServer transform(GameServerAddUpdateParam param) {
        return GameServer.builder()
                .id(param.getId())
                .gameId(param.getGameId())
                .serverId(param.getServerId())
                .serverName(param.getServerName())
                .nickName(param.getNickName())
                .startTime(param.getStartTime())
                .isDelete(DeleteEnum.NO.getCode())
                .createBy(param.getId() == null ? SecurityUtil.getUserId() : null)
                .createTime(param.getId() == null ? LocalDateTime.now() : null)
                .isSourceServer(param.getId() == null ? Boolean.TRUE : null)
                .isMerge(param.getId() == null ? Boolean.FALSE : null)
                .updateBy(SecurityUtil.getUserId())
                .updateTime(LocalDateTime.now())
                .build();
    }

    @Override
    public IPage<GameServerListVO> listOfPage(GameServerListParam param) {
        //游戏获取
        Tuple2<String, List<Long>> gameTuple = gameAuthService.getUserGameList(null);
        List<Long> gameIdList = gameTuple.getT2();
        if (CollectionUtils.isEmpty(gameIdList)) {
            return new Page<>();
        }
        //不包含参数游戏
        if (param.getGameId() != null && !gameIdList.contains(param.getGameId())) {
            return new Page<>();
        }
        Long gameId = null;
        if (param.getGameId() != null) {
            GameDTO gameDTO = gameService.getById(param.getGameId());
            if (gameDTO == null || gameDTO.getSuperGameId() == null) {
                return new Page<>();
            }
            gameId = gameDTO.getSuperGameId();
        }
        return page(param.toPage(), new QueryWrapper<GameServer>().lambda()
                .eq(gameId != null, GameServer::getGameId, gameId)
                .eq(Strings.isNotBlank(param.getServerId()), GameServer::getServerId, param.getServerId())
                .like(Strings.isNotBlank(param.getServerName()), GameServer::getServerName, param.getServerName())
                .like(Strings.isNotBlank(param.getNickName()), GameServer::getNickName, param.getNickName())
                .ge(param.getStartTime() != null, GameServer::getStartTime, param.getStartTime() == null ? null : LocalDateTime.of(param.getStartTime(), LocalTime.MIN))
                .le(param.getEndTime() != null, GameServer::getStartTime, param.getEndTime() == null ? null : LocalDateTime.of(param.getEndTime(), LocalTime.MAX))
                .eq(param.getIsSourceServer() != null, GameServer::getIsSourceServer, param.getIsSourceServer())
                .orderByDesc(GameServer::getStartTime)
                .orderByDesc(GameServer::getCreateTime)
        ).convert(this::toVo);
    }

    @Override
    public void getGameServerExcel(GameServerListParam param, HttpServletResponse response) {
        //游戏获取
        Tuple2<String, List<Long>> gameTuple = gameAuthService.getUserGameList(null);
        List<Long> gameIdList = gameTuple.getT2();
        if (CollectionUtils.isEmpty(gameIdList)) {
            return;
        }
        //不包含参数游戏
        if (param.getGameId() != null && !gameIdList.contains(param.getGameId())) {
            return;
        }
        Long gameId = null;
        if (param.getGameId() != null) {
            GameDTO gameDTO = gameService.getById(param.getGameId());
            if (gameDTO == null || gameDTO.getSuperGameId() == null) {
                return;
            }
            gameId = gameDTO.getSuperGameId();
        }
        final Long superGameId = gameId;
        //excel下载
        WebExcelUtil.httpExport(response, "游戏区服", GameServerExcelVO.class, ((pageNumber, pageSize) -> {
            //查询订单
            List<GameServer> gameServerList = page(new Page<>(pageNumber, pageSize),
                    new QueryWrapper<GameServer>().lambda()
                            .eq(Strings.isNotBlank(param.getServerId()), GameServer::getServerId, param.getServerId())
                            .eq(superGameId != null, GameServer::getGameId, superGameId)
                            .like(Strings.isNotBlank(param.getServerName()), GameServer::getServerName, param.getServerName())
                            .like(Strings.isNotBlank(param.getNickName()), GameServer::getNickName, param.getNickName())
                            .ge(param.getStartTime() != null, GameServer::getStartTime, param.getStartTime() == null ? null : LocalDateTime.of(param.getStartTime(), LocalTime.MIN))
                            .le(param.getEndTime() != null, GameServer::getStartTime, param.getEndTime() == null ? null : LocalDateTime.of(param.getEndTime(), LocalTime.MAX))
                            .eq(param.getIsSourceServer() != null, GameServer::getIsSourceServer, param.getIsSourceServer())
                            .orderByDesc(GameServer::getCreateTime)).getRecords();
            //excel字段赋值
            return gameServerList.stream().map(gameServer -> BeanUtil.copy(gameServer, GameServerExcelVO.class)).collect(Collectors.toList());
        }), ExcelUtil.DEFAULT_MAX_DATA_SIZE);
    }

    private GameServerListVO toVo(GameServer gameServer) {
        if (Objects.isNull(gameServer)) {
            return null;
        }
        GameServerListVO gameServerListVO = BeanUtil.copy(gameServer, GameServerListVO.class);
        GameSupper gameSupper = gameSupperService.getById(gameServer.getGameId());
        gameServerListVO.setGameName(gameSupper == null ? null : gameSupper.getName());
        //指派客服
        if (Strings.isNotBlank(gameServer.getCustomerIds())) {
            gameServerListVO.setCustomerList(this.getUserBeanList(gameServer.getCustomerIds()));
        }
        //指派GS
        if (Strings.isNotBlank(gameServer.getGsIds())) {
            gameServerListVO.setGsList(this.getUserBeanList(gameServer.getGsIds()));
        }
        //原始区服, 直接返回
        if (Objects.equals(gameServer.getIsSourceServer(), Boolean.TRUE)) {
            return gameServerListVO;
        }
        //合服
        List<String> serverIdList = new ArrayList<>();
        String[] sonServerIdArray = null;
        String[] sourceServerIdArray = null;
        if (Strings.isNotBlank(gameServer.getMainServerId())) {
            serverIdList.add(gameServer.getMainServerId());
        }
        if (Strings.isNotBlank(gameServer.getSonServerIds())) {
            sonServerIdArray = gameServer.getSonServerIds().split(",");
            Collections.addAll(serverIdList, sonServerIdArray);
        }
        if (Strings.isNotBlank(gameServer.getSourceServerIds())) {
            sourceServerIdArray = gameServer.getSourceServerIds().split(",");
            Collections.addAll(serverIdList, sourceServerIdArray);
        }
        //关联区服信息
        Map<String, GameServer> gameServerMap = super.list(new LambdaQueryWrapper<GameServer>()
                .eq(GameServer::getGameId, gameServer.getGameId())
                .in(GameServer::getServerId, serverIdList)
        ).stream().collect(Collectors.toMap(GameServer::getServerId, Function.identity()));
        //合服主服名称
        if (Strings.isNotBlank(gameServer.getMainServerId())) {
            GameServer mainServer = gameServerMap.get(gameServer.getMainServerId());
            gameServerListVO.setMainServerName(mainServer == null ? null : mainServer.getServerName());
        }
        //合服子服列表
        if (sonServerIdArray != null) {
            gameServerListVO.setSonServerList(this.getSonServerList(sonServerIdArray, gameServerMap));
        }
        //合服原始服列表
        if (sourceServerIdArray != null) {
            gameServerListVO.setSourceServerList(this.getSourceServerList(sourceServerIdArray, gameServerMap));
        }
        return gameServerListVO;
    }

    private List<GameServerListVO.ServerBean> getSonServerList(String[] sonServerIdArray, Map<String, GameServer> gameServerMap) {
        List<GameServerListVO.ServerBean> sonServerList = new ArrayList<>();
        for (String sonServerId : sonServerIdArray) {
            GameServer sonServer = gameServerMap.get(sonServerId);
            if (sonServer == null) {
                continue;
            }
            sonServerList.add(GameServerListVO.ServerBean.builder()
                    .serverId(sonServerId)
                    .serverName(sonServer.getServerName())
                    .build());
        }
        return sonServerList;
    }

    private List<GameServerListVO.ServerBean> getSourceServerList(String[] sourceServerIdArray, Map<String, GameServer> gameServerMap) {
        List<Integer> sourceServerIdList = Arrays.stream(sourceServerIdArray)
                .map(Integer::parseInt)
                .sorted().collect(Collectors.toList());
        List<GameServerListVO.ServerBean> sourceServerList = new ArrayList<>();
        sourceServerIdList.forEach(sourceServerId -> {
            GameServer sourceServer = gameServerMap.get(sourceServerId.toString());
            if (sourceServer == null) {
                return;
            }
            sourceServerList.add(GameServerListVO.ServerBean.builder()
                    .serverId(sourceServerId.toString())
                    .serverName(sourceServer.getServerName())
                    .build());
        });
        return sourceServerList;
    }

    private List<GameServerListVO.UserBean> getUserBeanList(String userIds) {
        List<GameServerListVO.UserBean> userList = new ArrayList<>();
        List<Long> userIdList = Arrays.stream(userIds.split(","))
                .map(Long::valueOf).collect(Collectors.toList());
        Map<Long, String> userNameMap = sysUserRpc.getUserNameByIds(userIdList).getData();
        userIdList.forEach(userId -> userList.add(GameServerListVO.UserBean.builder()
                .userId(userId)
                .userName(userNameMap.get(userId))
                .build()));
        return userList;
    }

    @Override
    public Boolean mergeServerAddUpdate(GameMergeServerParam param) {
        List<GameServer> gameServerList = super.list(new LambdaQueryWrapper<GameServer>()
                .eq(GameServer::getGameId, param.getGameId())
                .in(GameServer::getServerId, param.getServerIdList()));
        Set<String> sourceServerIds = new HashSet<>();
        gameServerList.forEach(gameServer -> {
            if (gameServer.getIsSourceServer()) {
                sourceServerIds.add(gameServer.getServerId());
                return;
            }
            if (Strings.isNotBlank(gameServer.getSourceServerIds())) {
                Collections.addAll(sourceServerIds, gameServer.getSourceServerIds().split(","));
            }
        });
        //新增
        if (param.getId() == null) {
            super.save(this.transform(param, sourceServerIds));
        } else {
            super.update(new LambdaUpdateWrapper<GameServer>()
                    .set(GameServer::getGameId, param.getGameId())
                    .set(GameServer::getMainServerId, param.getMainServerId())
                    .set(GameServer::getSonServerIds, String.join(",", param.getServerIdList()))
                    .set(GameServer::getSourceServerIds, String.join(",", sourceServerIds))
                    .set(GameServer::getServerName, param.getServerName())
                    .set(GameServer::getMergeTime, param.getMergeTime())
                    .set(GameServer::getUpdateBy, SecurityUtil.getUserId())
                    .set(GameServer::getUpdateTime, LocalDateTime.now())
                    .eq(GameServer::getId, param.getId()));
        }
        return super.update(new LambdaUpdateWrapper<GameServer>()
                .set(GameServer::getIsMerge, Boolean.TRUE)
                .set(GameServer::getUpdateTime, LocalDateTime.now())
                .eq(GameServer::getGameId, param.getGameId())
                .in(GameServer::getServerId, param.getServerIdList()));
    }

    private GameServer transform(GameMergeServerParam param, Set<String> sourceServerIds) {
        return GameServer.builder()
                .gameId(param.getGameId())
                .serverId(UUID.randomUUID().toString())
                .serverName(param.getServerName())
                .isSourceServer(Boolean.FALSE)
                .mainServerId(param.getMainServerId())
                .isMerge(Boolean.FALSE)
                .sonServerIds(String.join(",", param.getServerIdList()))
                .sourceServerIds(String.join(",", sourceServerIds))
                .mergeTime(param.getMergeTime())
                .startTime(param.getMergeTime())
                .isDelete(DeleteEnum.NO.getCode())
                .createBy(SecurityUtil.getUserId())
                .createTime(LocalDateTime.now())
                .updateBy(SecurityUtil.getUserId())
                .updateTime(LocalDateTime.now())
                .build();
    }

    @Override
    public List<GameServerVO> getServerList(Long gameId, Boolean isMerge) {
        return super.list(new LambdaQueryWrapper<GameServer>()
                .select(GameServer::getServerId, GameServer::getServerName, GameServer::getIsSourceServer)
                .eq(GameServer::getGameId, gameId)
                .eq(isMerge != null, GameServer::getIsMerge, isMerge)
                .orderByAsc(GameServer::getStartTime)
                .orderByAsc(GameServer::getCreateTime)
        ).stream().map(gameServer -> BeanUtil.copy(gameServer, GameServerVO.class))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(Long id) {
        return super.removeById(id);
    }
}
