package com.zanxiang.game.back.serve.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.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zanxiang.game.back.serve.dao.mapper.GameOceanengineUserLogMapper;
import com.zanxiang.game.back.serve.oceanengine.MiniGameCallback;
import com.zanxiang.game.back.serve.oceanengine.OceanengineCallbackException;
import com.zanxiang.game.back.serve.pojo.dto.GameOceanengineUserLogDTO;
import com.zanxiang.game.back.serve.pojo.entity.GameBackPolicy;
import com.zanxiang.game.back.serve.pojo.entity.GameOceanengineBackLog;
import com.zanxiang.game.back.serve.pojo.entity.GameOceanengineUserLog;
import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
import com.zanxiang.game.back.serve.pojo.vo.GameOceanengineUserLogVO;
import com.zanxiang.game.back.serve.service.IGameBackPolicyService;
import com.zanxiang.game.back.serve.service.IGameOceanengineBackLogService;
import com.zanxiang.game.back.serve.service.IGameOceanengineUserLogService;
import com.zanxiang.game.module.base.ServerInfo;
import com.zanxiang.game.module.base.pojo.vo.AgentRpcVO;
import com.zanxiang.game.module.base.rpc.IAgentRpc;
import com.zanxiang.module.util.bean.BeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
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.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GameOceanengineUserLogServiceImpl extends ServiceImpl<GameOceanengineUserLogMapper, GameOceanengineUserLog>
        implements IGameOceanengineUserLogService {

    @Autowired
    private IGameBackPolicyService gameBackPolicyService;
    @Autowired
    private MiniGameCallback miniGameCallback;
    @Autowired
    private IGameOceanengineBackLogService gameOceanengineBackLogService;
    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private IAgentRpc agentRpc;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean callback(GameOceanengineUserLog userLog, boolean mustBack) {
        BackStatusEnum backStatus = doCallback(userLog, mustBack);
        return update(new LambdaUpdateWrapper<GameOceanengineUserLog>()
                .set(GameOceanengineUserLog::getBackStatus, backStatus.getBackStatus())
                .eq(GameOceanengineUserLog::getId, userLog.getId())
        );
    }

    @Override
    public IPage<GameOceanengineUserLogVO> oceanengineUserLogList(GameOceanengineUserLogDTO dto) {
        IPage<GameOceanengineUserLog> page = page(dto.toPage(), new LambdaQueryWrapper<GameOceanengineUserLog>()
                .eq(StringUtils.isNotBlank(dto.getAgentKey()), GameOceanengineUserLog::getAgentKey, dto.getAgentKey())
                .ge(dto.getActiveTimeBegin() != null, GameOceanengineUserLog::getActiveTime, dto.getActiveTimeBegin() == null ? null : LocalDateTime.of(dto.getActiveTimeBegin(), LocalTime.MIDNIGHT))
                .le(dto.getActiveTimeEnd() != null, GameOceanengineUserLog::getActiveTime, dto.getActiveTimeEnd() == null ? null : LocalDateTime.of(dto.getActiveTimeEnd(), LocalTime.MAX))
                .eq(null != dto.getGameId(), GameOceanengineUserLog::getGameId, dto.getGameId())
                .eq(null != dto.getAccountId(), GameOceanengineUserLog::getAccountId, dto.getAccountId())
                .eq(StringUtils.isNotBlank(dto.getAppId()), GameOceanengineUserLog::getAppId, dto.getAppId())
                .eq(StringUtils.isNotBlank(dto.getOpenId()), GameOceanengineUserLog::getOpenId, dto.getOpenId())
                .eq(null != dto.getBackStatus(), GameOceanengineUserLog::getBackStatus, dto.getBackStatus())
                .eq(null != dto.getProjectId(), GameOceanengineUserLog::getProjectId, dto.getProjectId())
                .eq(null != dto.getPromotionId(), GameOceanengineUserLog::getPromotionId, dto.getPromotionId())
                .orderByDesc(GameOceanengineUserLog::getCreateTime)
        );
        IPage<GameOceanengineUserLogVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return result;
        }
        result.setRecords(toVOBatch(page.getRecords()));
        return result;
    }

    private List<GameOceanengineUserLogVO> toVOBatch(List<GameOceanengineUserLog> logList) {
        if (CollectionUtils.isEmpty(logList)) {
            return Collections.emptyList();
        }
        Set<String> agentKeys = logList.stream().map(GameOceanengineUserLog::getAgentKey).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
        Map<String, AgentRpcVO> agentMap = CollectionUtils.isEmpty(agentKeys) ? Collections.emptyMap() : agentRpc.getByAgentKeys(new ArrayList<>(agentKeys))
                .getData().stream().collect(Collectors.toMap(AgentRpcVO::getAgentKey, Function.identity()));
        return logList.stream().map(log -> {
            GameOceanengineUserLogVO vo = BeanUtil.copy(log, GameOceanengineUserLogVO.class);
            vo.setProjectId(log.getProjectId() == null ? null : log.getProjectId().toString());
            vo.setPromotionId(log.getPromotionId() == null ? null : log.getPromotionId().toString());
            if (StringUtils.isNotBlank(log.getAgentKey())) {
                AgentRpcVO agent = agentMap.get(log.getAgentKey());
                if (agent != null) {
                    vo.setAgentName(agent.getAgentName());
                }
            }
            return vo;
        }).collect(Collectors.toList());

    }

    @Override
    public boolean oceanengineUserReport(List<Long> ids) {
        listByIds(ids).forEach(userLog -> callback(userLog, true));
        return true;
    }

    private BackStatusEnum doCallback(GameOceanengineUserLog userLog, boolean mustBack) {
        // 是否已经回传过
        if (!mustBack && gameOceanengineBackLogService.count(new LambdaQueryWrapper<GameOceanengineBackLog>()
                .eq(GameOceanengineBackLog::getGameId, userLog.getGameId())
                .eq(GameOceanengineBackLog::getAppId, userLog.getAppId())
                .eq(GameOceanengineBackLog::getOpenId, userLog.getOpenId())
                .eq(GameOceanengineBackLog::getAccountId, userLog.getAccountId())
                .eq(GameOceanengineBackLog::getEventType, MiniGameCallback.EventType.ACTIVE.getEventType())
                .eq(GameOceanengineBackLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
        ) > 0) {
            return BackStatusEnum.NO;
        }
        MiniGameCallback.MiniGameCallbackRequest request = MiniGameCallback.MiniGameCallbackRequest.builder()
                .clue_token(userLog.getClueToken())
                .open_id(userLog.getOpenId())
                .union_id(userLog.getUnionId())
                .event_type(String.valueOf(MiniGameCallback.EventType.ACTIVE.getEventType()))
                .build();
        BackStatusEnum backStatus = BackStatusEnum.SUCCESS;
        String errMsg = null;
        try {
            miniGameCallback.doCallback(userLog.getAccountReportUrl(), userLog.getAccountReportToken(), request);
        } catch (OceanengineCallbackException e) {
            log.error(e.getMessage(), e);
            backStatus = BackStatusEnum.FAILED;
            errMsg = e.getMsg();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            backStatus = BackStatusEnum.FAILED;
            errMsg = "系统异常：" + e.getMessage();
        }
        gameOceanengineBackLogService.save(GameOceanengineBackLog.builder()
                .appId(userLog.getAppId())
                .openId(userLog.getOpenId())
                .unionId(userLog.getUnionId())
                .gameId(userLog.getGameId())
                .accountId(userLog.getAccountId())
                .clueToken(userLog.getClueToken())
                .eventType(request.getEvent_type())
                .eventLogId(userLog.getId())
                .createTime(LocalDateTime.now())
                .backStatus(backStatus.getBackStatus())
                .errMsg(errMsg)
                .build());
        //判断是否开启注册回传创角
        try {
            this.regCallBackRole(userLog);
        } catch (Exception ignored) {
        }
        //返回回传结果
        return backStatus;
    }

    private void regCallBackRole(GameOceanengineUserLog userLog) {
        GameBackPolicy gameBackPolicy = userLog.getBackPolicyId() == null ? null
                : gameBackPolicyService.getById(userLog.getBackPolicyId());
        //判断策略是否控制注册回传创角
        if (gameBackPolicy == null || !Objects.equals(gameBackPolicy.getRoleBackSwitch(), Boolean.TRUE)) {
            return;
        }
        //创角回传
        MiniGameCallback.MiniGameCallbackRequest request = MiniGameCallback.MiniGameCallbackRequest.builder()
                .clue_token(userLog.getClueToken())
                .open_id(userLog.getOpenId())
                .union_id(userLog.getUnionId())
                .event_type(String.valueOf(MiniGameCallback.EventType.ACTIVE_REGISTER.getEventType()))
                .build();
        BackStatusEnum backStatus = BackStatusEnum.SUCCESS;
        String errMsg = null;
        try {
            miniGameCallback.doCallback(userLog.getAccountReportUrl(), userLog.getAccountReportToken(), request);
        } catch (OceanengineCallbackException e) {
            log.error(e.getMessage(), e);
            backStatus = BackStatusEnum.FAILED;
            errMsg = e.getMsg();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            backStatus = BackStatusEnum.FAILED;
            errMsg = "系统异常：" + e.getMessage();
        }
        gameOceanengineBackLogService.save(GameOceanengineBackLog.builder()
                .appId(userLog.getAppId())
                .openId(userLog.getOpenId())
                .unionId(userLog.getUnionId())
                .gameId(userLog.getGameId())
                .accountId(userLog.getAccountId())
                .clueToken(userLog.getClueToken())
                .eventType(request.getEvent_type())
                .eventLogId(userLog.getId())
                .createTime(LocalDateTime.now())
                .backStatus(backStatus.getBackStatus())
                .errMsg(errMsg)
                .build());
    }
}
