package com.zanxiang.game.module.manage.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.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
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.pojo.dto.GameDTO;
import com.zanxiang.game.module.manage.pojo.params.RoleAssignRecordAddParam;
import com.zanxiang.game.module.manage.pojo.params.RoleAssignRecordListParam;
import com.zanxiang.game.module.manage.pojo.params.RoleInfoAndAgentParam;
import com.zanxiang.game.module.manage.pojo.params.RoleSysUserAssignParam;
import com.zanxiang.game.module.manage.pojo.vo.RoleAssignRecordListVO;
import com.zanxiang.game.module.manage.service.*;
import com.zanxiang.game.module.mybatis.entity.*;
import com.zanxiang.game.module.mybatis.mapper.RoleAssignRecordMapper;
import com.zanxiang.module.util.exception.BaseException;
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.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 角色指派记录表 服务实现类
 * </p>
 *
 * @author auto
 * @since 2023-09-09
 */
@Service
public class RoleAssignRecordServiceImpl extends ServiceImpl<RoleAssignRecordMapper, RoleAssignRecord> implements IRoleAssignRecordService {

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

    @Autowired
    private IAgentService agentService;

    @Autowired
    private IGameService gameService;

    @Autowired
    private IGameServerService gameServerService;

    @Autowired
    private IGameUserRoleService gameUserRoleService;

    @Autowired
    private IRoleOperateService roleOperateService;

    @Override
    public void updateStatusSync() {
        List<RoleAssignRecord> recordList = super.list();
        //数据分组
        Map<String, List<RoleAssignRecord>> map = new HashMap<>();
        recordList.forEach(record -> {
            String key = record.getGameId() + "_" + record.getServerId() + "_" + record.getRoleId();
            if (map.containsKey(key)) {
                map.get(key).add(record);
            } else {
                List<RoleAssignRecord> list = new ArrayList<>();
                list.add(record);
                map.put(key, list);
            }
        });
        //循环处理
        map.forEach((key, list) -> {
            //排序
            list.sort(Comparator.comparing(RoleAssignRecord::getStartTime));
            //循环判断
            Long tempGsId = null;
            Long tempOperUserId = null;
            Long tempCustomerServiceId = null;
            for (RoleAssignRecord r : list) {
                r.setIsGsUpdate(!Objects.equals(r.getGsId(), tempGsId));
                r.setIsOperUpdate(!Objects.equals(r.getOperUserId(), tempOperUserId));
                r.setIsCustomerUpdate(!Objects.equals(r.getCustomerServiceId(), tempCustomerServiceId));
                tempGsId = r.getGsId();
                tempOperUserId = r.getOperUserId();
                tempCustomerServiceId = r.getCustomerServiceId();
            }
            super.updateBatchById(list);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean configSysUser(RoleSysUserAssignParam param) {
        Long sysUserId = SecurityUtil.getUserId();
        LocalDateTime now = LocalDateTime.now();
        boolean singleConfig = param.getRoleInfoAndAgentParamList().size() == 1;
        List<RoleAssignRecord> lastAssignRecordList = new ArrayList<>();
        List<RoleAssignRecord> insertAssignRecordList = new ArrayList<>();
        param.getRoleInfoAndAgentParamList().forEach(item -> {
            RoleAssignRecord lastAssignRecord = lastAssignRecord(item);
            RoleAssignRecord insertRoleAssignRecord = RoleAssignRecord.builder()
                    .gameId(item.getGameId())
                    .serverId(item.getServerId())
                    .userId(item.getUserId())
                    .roleId(item.getRoleId())
                    .regAgentId(item.getRegAgentId())
                    .startTime(param.getStartTime())
                    .gsId(param.getGsId())
                    .customerServiceId(param.getCustomerServiceId())
                    .operUserId(param.getOperUserId())
                    .remark(param.getRemark())
                    .isGsUpdate(lastAssignRecord == null ? Boolean.TRUE : param.getGsId() != null && !Objects.equals(param.getGsId(), lastAssignRecord.getGsId()))
                    .isOperUpdate(lastAssignRecord == null ? Boolean.TRUE : param.getOperUserId() != null && !Objects.equals(param.getOperUserId(), lastAssignRecord.getOperUserId()))
                    .isCustomerUpdate(lastAssignRecord == null ? Boolean.TRUE : param.getCustomerServiceId() != null && !Objects.equals(param.getCustomerServiceId(), lastAssignRecord.getCustomerServiceId()))
                    .createBy(sysUserId)
                    .createTime(now)
                    .updateBy(sysUserId)
                    .updateTime(now)
                    .build();
            if (null == lastAssignRecord) {
                insertAssignRecordList.add(insertRoleAssignRecord);
            } else {
                //存在记录，更新最后一条记录
                int compare = param.getStartTime().compareTo(lastAssignRecord.getStartTime());
                if (compare < 0) {
                    throw new BaseException("角色id:" + item.getRoleId() + ",开始时间不能早于上条指派记录的开始时间！！！");
                }
                lastAssignRecord.setUpdateBy(sysUserId);
                lastAssignRecord.setUpdateTime(now);
                if (compare == 0) {
                    //上一条记录
                    RoleAssignRecord previousAssignRecord = previousAssignRecord(lastAssignRecord, param.getStartTime());
                    //记录是否变更参数
                    lastAssignRecord.setIsGsUpdate(previousAssignRecord == null ? Boolean.TRUE : param.getGsId() != null && !Objects.equals(param.getGsId(), previousAssignRecord.getGsId()));
                    lastAssignRecord.setIsOperUpdate(previousAssignRecord == null ? Boolean.TRUE : param.getOperUserId() != null && !Objects.equals(param.getOperUserId(), previousAssignRecord.getOperUserId()));
                    lastAssignRecord.setIsCustomerUpdate(previousAssignRecord == null ? Boolean.TRUE : param.getCustomerServiceId() != null && !Objects.equals(param.getCustomerServiceId(), previousAssignRecord.getCustomerServiceId()));
                    //参数变更
                    lastAssignRecord.setGsId(singleConfig ? param.getGsId() : null != param.getGsId() ? param.getGsId() : lastAssignRecord.getGsId());
                    lastAssignRecord.setOperUserId(singleConfig ? param.getOperUserId() : null != param.getOperUserId() ? param.getOperUserId() : lastAssignRecord.getOperUserId());
                    lastAssignRecord.setCustomerServiceId(singleConfig ? param.getCustomerServiceId() : null != param.getCustomerServiceId() ? param.getCustomerServiceId() : lastAssignRecord.getCustomerServiceId());
                    lastAssignRecord.setRemark(singleConfig ? param.getRemark() : null != param.getRemark() ? param.getRemark() : lastAssignRecord.getRemark());
                } else {
                    lastAssignRecord.setEndTime(param.getStartTime());
                    insertAssignRecordList.add(insertRoleAssignRecord);
                }
                lastAssignRecordList.add(lastAssignRecord);
            }
        });
        if (CollectionUtils.isNotEmpty(lastAssignRecordList)) {
            updateBatchById(lastAssignRecordList);
        }
        if (CollectionUtils.isNotEmpty(insertAssignRecordList)) {
            saveBatch(insertAssignRecordList);
        }
        //角色操作表更新
        List<RoleOperate> roleOperateList = param.getRoleInfoAndAgentParamList().stream()
                .map(this::getModifyRoleOperate).collect(Collectors.toList());
        return roleOperateService.saveOrUpdateBatch(roleOperateList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean modify(RoleAssignRecordAddParam param) {
        Long sysUserId = SecurityUtil.getUserId();
        LocalDateTime now = LocalDateTime.now();
        RoleAssignRecord changeRecord = getById(param.getId());
        RoleAssignRecord previousAssignRecord = previousAssignRecord(changeRecord, param.getStartTime());
        changeRecord.setIsCustomerUpdate(previousAssignRecord == null ? Boolean.TRUE : param.getCustomerServiceId() != null && !Objects.equals(param.getCustomerServiceId(), previousAssignRecord.getCustomerServiceId()));
        changeRecord.setIsOperUpdate(previousAssignRecord == null ? Boolean.TRUE : param.getOperUserId() != null && !Objects.equals(param.getOperUserId(), previousAssignRecord.getOperUserId()));
        changeRecord.setIsGsUpdate(previousAssignRecord == null ? Boolean.TRUE : param.getGsId() != null && !Objects.equals(param.getGsId(), previousAssignRecord.getGsId()));
        changeRecord.setStartTime(param.getStartTime());
        changeRecord.setOperUserId(param.getOperUserId());
        changeRecord.setGsId(param.getGsId());
        changeRecord.setCustomerServiceId(param.getCustomerServiceId());
        changeRecord.setRemark(param.getRemark());
        changeRecord.setUpdateBy(sysUserId);
        changeRecord.setUpdateTime(now);
        if (previousAssignRecord != null) {
            if (previousAssignRecord.getStartTime().compareTo(changeRecord.getStartTime()) >= 0) {
                throw new BaseException("开始时间必须大于下一条记录的开始时间！");
            }
            update(new LambdaUpdateWrapper<RoleAssignRecord>()
                    .set(RoleAssignRecord::getEndTime, changeRecord.getStartTime())
                    .eq(RoleAssignRecord::getId, previousAssignRecord.getId())
            );
        }
        updateById(changeRecord);
        //角色操作表更新
        RoleInfoAndAgentParam roleInfoAndAgentParam = RoleInfoAndAgentParam.builder()
                .regAgentId(changeRecord.getRegAgentId())
                .gameId(changeRecord.getGameId())
                .serverId(changeRecord.getServerId())
                .userId(changeRecord.getUserId())
                .roleId(changeRecord.getRoleId())
                .build();
        RoleOperate modifyRoleOperate = getModifyRoleOperate(roleInfoAndAgentParam);
        return roleOperateService.saveOrUpdate(modifyRoleOperate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public IPage<RoleAssignRecordListVO> listOfPage(RoleAssignRecordListParam param) {
        Agent agent = agentService.getById(param.getRegAgentId());
        GameDTO game = gameService.getById(param.getGameId());
        Long superGameId = null == game.getSuperGameId() ? param.getGameId() : game.getSuperGameId();
        GameServer gameServer = gameServerService.getOne(new LambdaQueryWrapper<GameServer>()
                .eq(GameServer::getGameId, superGameId)
                .eq(GameServer::getServerId, param.getServerId()));
        GameUserRole role = gameUserRoleService.getOne(new LambdaQueryWrapper<GameUserRole>()
                .eq(GameUserRole::getGameId, param.getGameId())
                .eq(GameUserRole::getServerId, param.getServerId())
                .eq(GameUserRole::getRoleId, param.getRoleId()));

        return page(param.toPage(), new LambdaQueryWrapper<RoleAssignRecord>()
                .eq(RoleAssignRecord::getRegAgentId, param.getRegAgentId())
                .eq(RoleAssignRecord::getGameId, param.getGameId())
                .eq(RoleAssignRecord::getServerId, param.getServerId())
                .eq(RoleAssignRecord::getUserId, param.getUserId())
                .eq(RoleAssignRecord::getRoleId, param.getRoleId())
                .like(StringUtils.isNotBlank(param.getRemark()), RoleAssignRecord::getRemark, param.getRemark())
                .eq(null != param.getGsId(), RoleAssignRecord::getGsId, param.getGsId())
                .eq(null != param.getOperUserId(), RoleAssignRecord::getOperUserId, param.getOperUserId())
                .eq(null != param.getCustomerServiceId(), RoleAssignRecord::getCustomerServiceId, param.getCustomerServiceId())
                .eq(RoleAssignRecord::getIsDelete, 0)
                .orderByDesc(RoleAssignRecord::getCreateTime))
                .convert(item -> RoleAssignRecordListVO.builder()
                        .id(item.getId())
                        .gameId(item.getGameId())
                        .gameName(null == game ? null : game.getName())
                        .serverId(item.getServerId())
                        .serverName(null == gameServer ? null : gameServer.getServerName())
                        .userId(item.getUserId())
                        .roleId(item.getRoleId())
                        .roleName(null == role ? null : role.getRoleName())
                        .regAgentId(null == agent ? null : agent.getAgentName())
                        .startTime(item.getStartTime())
                        .endTime(item.getEndTime())
                        .gsId(item.getGsId())
                        .gsName(null == item.getGsId() ? null : getNickname(item.getGsId()))
                        .operUserId(item.getOperUserId())
                        .operUserName(null == item.getOperUserId() ? null : getNickname(item.getOperUserId()))
                        .customerServiceId(item.getCustomerServiceId())
                        .customerServiceName(null == item.getCustomerServiceId() ? null : getNickname(item.getCustomerServiceId()))
                        .remark(item.getRemark())
                        .createBy(item.getCreateBy())
                        .createName(null == item.getCreateBy() ? null : getNickname(item.getCreateBy()))
                        .createTime(item.getCreateTime())
                        .updateBy(item.getUpdateBy())
                        .updateName(null == item.getUpdateBy() ? null : getNickname(item.getUpdateBy()))
                        .updateTime(item.getUpdateTime())
                        .build());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(Long id) {
        Long sysUserId = SecurityUtil.getUserId();
        LocalDateTime now = LocalDateTime.now();
        RoleAssignRecord changeRecord = getById(id);
        RoleAssignRecord nextRecord = getNextRecord(changeRecord, changeRecord.getStartTime());
        RoleInfoAndAgentParam roleInfoAndAgentParam = RoleInfoAndAgentParam.builder()
                .regAgentId(changeRecord.getRegAgentId())
                .gameId(changeRecord.getGameId())
                .serverId(changeRecord.getServerId())
                .userId(changeRecord.getUserId())
                .roleId(changeRecord.getRoleId())
                .build();
        RoleAssignRecord previousRecord = previousAssignRecord(changeRecord, changeRecord.getStartTime());
        if (previousRecord != null) {
            if (nextRecord != null) {
                update(new LambdaUpdateWrapper<RoleAssignRecord>()
                        .set(RoleAssignRecord::getEndTime, nextRecord.getStartTime())
                        .eq(RoleAssignRecord::getId, previousRecord.getId()));
            } else {
                update(new LambdaUpdateWrapper<RoleAssignRecord>()
                        .set(RoleAssignRecord::getEndTime, null)
                        .eq(RoleAssignRecord::getId, previousRecord.getId()));
            }
        }

        update(new LambdaUpdateWrapper<RoleAssignRecord>()
                .set(RoleAssignRecord::getIsDelete, 1)
                .set(RoleAssignRecord::getUpdateBy, sysUserId)
                .set(RoleAssignRecord::getUpdateTime, now)
                .eq(RoleAssignRecord::getId, id));
        RoleAssignRecord roleAssignRecord = getById(id);
        if (null != roleAssignRecord) {
            RoleOperate modifyRoleOperate = getModifyRoleOperate(roleInfoAndAgentParam);
            return roleOperateService.saveOrUpdate(modifyRoleOperate);
        }
        return true;
    }

    /**
     * 最后记录
     *
     * @param item
     * @return
     */
    private RoleAssignRecord lastAssignRecord(RoleInfoAndAgentParam item) {
        return getOne(new LambdaQueryWrapper<RoleAssignRecord>()
                .eq(RoleAssignRecord::getRegAgentId, item.getRegAgentId())
                .eq(RoleAssignRecord::getGameId, item.getGameId())
                .eq(RoleAssignRecord::getServerId, item.getServerId())
                .eq(RoleAssignRecord::getUserId, item.getUserId())
                .eq(RoleAssignRecord::getRoleId, item.getRoleId())
                .orderByDesc(RoleAssignRecord::getStartTime)
                .eq(RoleAssignRecord::getIsDelete, 0)
                .last("limit 1"));
    }

    /**
     * 上一条记录
     *
     * @return
     */
    private RoleAssignRecord previousAssignRecord(RoleAssignRecord item, LocalDate startTime) {
        return getOne(new LambdaQueryWrapper<RoleAssignRecord>()
                .eq(RoleAssignRecord::getRegAgentId, item.getRegAgentId())
                .eq(RoleAssignRecord::getGameId, item.getGameId())
                .eq(RoleAssignRecord::getServerId, item.getServerId())
                .eq(RoleAssignRecord::getUserId, item.getUserId())
                .eq(RoleAssignRecord::getRoleId, item.getRoleId())
                .lt(RoleAssignRecord::getStartTime, startTime)
                .orderByDesc(RoleAssignRecord::getStartTime)
                .eq(RoleAssignRecord::getIsDelete, 0)
                .last("limit 1"));
    }

    /**
     * 下一条记录
     *
     * @return
     */
    private RoleAssignRecord getNextRecord(RoleAssignRecord item, LocalDate startTime) {
        return getOne(new LambdaQueryWrapper<RoleAssignRecord>()
                .eq(RoleAssignRecord::getRegAgentId, item.getRegAgentId())
                .eq(RoleAssignRecord::getGameId, item.getGameId())
                .eq(RoleAssignRecord::getServerId, item.getServerId())
                .eq(RoleAssignRecord::getUserId, item.getUserId())
                .eq(RoleAssignRecord::getRoleId, item.getRoleId())
                .gt(RoleAssignRecord::getStartTime, startTime)
                .orderByAsc(RoleAssignRecord::getStartTime)
                .eq(RoleAssignRecord::getIsDelete, 0)
                .last("limit 1")
        );
    }

    private String getNickname(Long sysUserId) {
        return sysUserRpc.getById(sysUserId).getData().getNickname();
    }

    private RoleOperate getModifyRoleOperate(RoleInfoAndAgentParam roleInfoAndAgentParam) {
        RoleAssignRecord lastAssignRecord = lastAssignRecord(roleInfoAndAgentParam);
        Long sysUserId = SecurityUtil.getUserId();
        LocalDateTime now = LocalDateTime.now();
        RoleOperate roleOperate = RoleOperate.builder().build();
        RoleOperate queryOne = roleOperateService.getOne(new LambdaQueryWrapper<RoleOperate>()
                .eq(RoleOperate::getGameId, roleInfoAndAgentParam.getGameId())
                .eq(RoleOperate::getServerId, roleInfoAndAgentParam.getServerId())
                .eq(RoleOperate::getUserId, roleInfoAndAgentParam.getUserId())
                .eq(RoleOperate::getRoleId, roleInfoAndAgentParam.getRoleId())
                .eq(RoleOperate::getIsDelete, 0));
        if (null == queryOne) {
            roleOperate.setGameId(roleInfoAndAgentParam.getGameId());
            roleOperate.setServerId(roleInfoAndAgentParam.getServerId());
            roleOperate.setUserId(roleInfoAndAgentParam.getUserId());
            roleOperate.setRoleId(roleInfoAndAgentParam.getRoleId());
            roleOperate.setCreateBy(sysUserId);
            roleOperate.setCreateTime(now);
        } else {
            roleOperate = queryOne;
        }
        roleOperate.setGsId(null == lastAssignRecord ? null : lastAssignRecord.getGsId());
        roleOperate.setCustomerServiceId(null == lastAssignRecord ? null : lastAssignRecord.getCustomerServiceId());
        roleOperate.setOperUserId(null == lastAssignRecord ? null : lastAssignRecord.getOperUserId());
        roleOperate.setRemark(null == lastAssignRecord ? null : lastAssignRecord.getRemark());
        roleOperate.setUpdateBy(sysUserId);
        roleOperate.setUpdateTime(now);
        return roleOperate;
    }
}
