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.github.sd4324530.jtuple.Tuple2;
import com.zanxiang.advertising.tencent.base.AdvertisingTencentServer;
import com.zanxiang.advertising.tencent.base.rpc.IUserActionSetRpc;
import com.zanxiang.game.back.serve.dao.mapper.GameTencentMiniGameUserMapper;
import com.zanxiang.game.back.serve.pojo.dto.GameTencentMiniGameUserDTO;
import com.zanxiang.game.back.serve.pojo.entity.GameBackPolicy;
import com.zanxiang.game.back.serve.pojo.entity.GameTencentMiniGameBackLog;
import com.zanxiang.game.back.serve.pojo.entity.GameTencentMiniGameUser;
import com.zanxiang.game.back.serve.pojo.enums.ActionTypeEnum;
import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
import com.zanxiang.game.back.serve.pojo.enums.BackTypeEnum;
import com.zanxiang.game.back.serve.pojo.vo.GameTencentMiniGameUserVO;
import com.zanxiang.game.back.serve.service.IGameBackPolicyService;
import com.zanxiang.game.back.serve.service.IGameTencentMiniGameBackLogService;
import com.zanxiang.game.back.serve.service.IGameTencentMiniGameUserService;
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 GameTencentMiniGameUserServiceImpl extends ServiceImpl<GameTencentMiniGameUserMapper, GameTencentMiniGameUser>
        implements IGameTencentMiniGameUserService {

    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private IAgentRpc agentRpc;

    @DubboReference(providedBy = AdvertisingTencentServer.SERVER_DUBBO_NAME)
    private IUserActionSetRpc userActionSetRpc;

    @Autowired
    private IGameTencentMiniGameBackLogService gameTencentMiniGameBackLogService;

    @Autowired
    private IGameBackPolicyService gameBackPolicyService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean userBack(GameTencentMiniGameUser userLog) {
        if (StringUtils.isBlank(userLog.getClickId())) {
            // 没有点击 id（之后做监测链接，从监测链接里面找）
            return false;
        }
        BackStatusEnum backStatus = doCallback(userLog).first;
        return update(new LambdaUpdateWrapper<GameTencentMiniGameUser>()
                .set(GameTencentMiniGameUser::getBackStatus, backStatus.getBackStatus())
                .eq(GameTencentMiniGameUser::getId, userLog.getId())
        );
    }

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

    @Override
    public IPage<GameTencentMiniGameUserVO> listOfPage(GameTencentMiniGameUserDTO dto) {
        IPage<GameTencentMiniGameUser> page = page(dto.toPage(), new LambdaQueryWrapper<GameTencentMiniGameUser>()
                .eq(StringUtils.isNotBlank(dto.getAgentKey()), GameTencentMiniGameUser::getAgentKey, dto.getAgentKey())
                .ge(dto.getActiveTimeBegin() != null, GameTencentMiniGameUser::getRegisterTime, dto.getActiveTimeBegin() == null ? null : LocalDateTime.of(dto.getActiveTimeBegin(), LocalTime.MIDNIGHT))
                .le(dto.getActiveTimeEnd() != null, GameTencentMiniGameUser::getRegisterTime, dto.getActiveTimeEnd() == null ? null : LocalDateTime.of(dto.getActiveTimeEnd(), LocalTime.MAX))
                .eq(null != dto.getGameId(), GameTencentMiniGameUser::getGameId, dto.getGameId())
                .eq(null != dto.getAdAccountId(), GameTencentMiniGameUser::getAdAccountId, dto.getAdAccountId())
                .eq(StringUtils.isNotBlank(dto.getWechatAppId()), GameTencentMiniGameUser::getWechatAppId, dto.getWechatAppId())
                .eq(StringUtils.isNotBlank(dto.getWechatOpenid()), GameTencentMiniGameUser::getWechatOpenid, dto.getWechatOpenid())
                .eq(null != dto.getBackStatus(), GameTencentMiniGameUser::getBackStatus, dto.getBackStatus())
                .orderByDesc(GameTencentMiniGameUser::getCreateTime)
        );
        IPage<GameTencentMiniGameUserVO> result = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return result;
        }
        result.setRecords(toVOBatch(page.getRecords()));
        return result;
    }

    @Override
    public boolean doReport(List<Long> userLogIds) {
        listByIds(userLogIds).forEach(userLog -> {
            BackStatusEnum backStatus = doCallback(userLog).first;
            update(new LambdaUpdateWrapper<GameTencentMiniGameUser>()
                    .set(GameTencentMiniGameUser::getBackStatus, backStatus.getBackStatus())
                    .eq(GameTencentMiniGameUser::getId, userLog.getId())
            );
        });
        return true;
    }

    private List<GameTencentMiniGameUserVO> toVOBatch(List<GameTencentMiniGameUser> logList) {
        if (CollectionUtils.isEmpty(logList)) {
            return Collections.emptyList();
        }
        Set<String> agentKeys = logList.stream().map(GameTencentMiniGameUser::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 -> {
            GameTencentMiniGameUserVO vo = toVOSimple(log);
            if (StringUtils.isNotBlank(log.getAgentKey())) {
                AgentRpcVO agent = agentMap.get(log.getAgentKey());
                if (agent != null) {
                    vo.setAgentName(agent.getAgentName());
                }
            }
            return vo;
        }).collect(Collectors.toList());

    }

    private Tuple2<BackStatusEnum, String> doCallback(GameTencentMiniGameUser userLog) {
        //注册已回传判断
        if (gameTencentMiniGameBackLogService.count(new LambdaQueryWrapper<GameTencentMiniGameBackLog>()
                .eq(GameTencentMiniGameBackLog::getGameId, userLog.getGameId())
                .eq(GameTencentMiniGameBackLog::getWechatAppId, userLog.getWechatAppId())
                .eq(GameTencentMiniGameBackLog::getWechatOpenid, userLog.getWechatOpenid())
                .eq(GameTencentMiniGameBackLog::getAdAccountId, userLog.getAdAccountId())
                .eq(GameTencentMiniGameBackLog::getActionType, ActionTypeEnum.REGISTER.getActionType())
                .eq(GameTencentMiniGameBackLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                .eq(GameTencentMiniGameBackLog::getBackType, BackTypeEnum.BACK_API.getBackType())
        ) > 0) {
            //返回注册已回传
            return Tuple2.with(BackStatusEnum.NO, null);
        }
        //回传策略
        GameBackPolicy gameBackPolicy = userLog.getBackPolicyId() == null ? null
                : gameBackPolicyService.getById(userLog.getBackPolicyId());
        //设置了注册回传创角, 同时回传注册和创角
        if (gameBackPolicy != null && Objects.equals(gameBackPolicy.getRoleBackSwitch(), Boolean.TRUE)) {
            return gameTencentMiniGameBackLogService.userAndRoleRegisterBack(userLog);
        }
        //正常注册回传
        return gameTencentMiniGameBackLogService.userBack(userLog);
    }

    private GameTencentMiniGameUserVO toVOSimple(GameTencentMiniGameUser userLog) {
        if (userLog == null) {
            return null;
        }
        return BeanUtil.copy(userLog, GameTencentMiniGameUserVO.class);
    }

    private BackStatusEnum doCallbackMediaSdk(GameTencentMiniGameUser userLog) {
        if (gameTencentMiniGameBackLogService.count(new LambdaQueryWrapper<GameTencentMiniGameBackLog>()
                .eq(GameTencentMiniGameBackLog::getGameId, userLog.getGameId())
                .eq(GameTencentMiniGameBackLog::getWechatAppId, userLog.getWechatAppId())
                .eq(GameTencentMiniGameBackLog::getWechatOpenid, userLog.getWechatOpenid())
                .eq(GameTencentMiniGameBackLog::getAdAccountId, userLog.getAdAccountId())
                .eq(GameTencentMiniGameBackLog::getActionType, ActionTypeEnum.REGISTER.getActionType())
                .eq(GameTencentMiniGameBackLog::getBackStatus, BackStatusEnum.SUCCESS.getBackStatus())
                .eq(GameTencentMiniGameBackLog::getBackType, BackTypeEnum.BACK_MEDIA_SDK.getBackType())
        ) > 0) {
            return BackStatusEnum.NO;
        }
        BackStatusEnum backStatus = BackStatusEnum.SUCCESS;
        gameTencentMiniGameBackLogService.save(GameTencentMiniGameBackLog.builder()
                .gameId(userLog.getGameId())
                .adAccountId(userLog.getAdAccountId())
                .clickId(userLog.getClickId())
                .actionTime(LocalDateTime.now())
                .wechatOpenid(userLog.getWechatOpenid())
                .wechatAppId(userLog.getWechatAppId())
                .actionType(ActionTypeEnum.REGISTER.getActionType())
                .createTime(LocalDateTime.now())
                .backStatus(backStatus.getBackStatus())
                .errMsg(null)
                .backType(BackTypeEnum.BACK_MEDIA_SDK.getBackType())
                .build());
        return backStatus;
    }
}
