package com.zanxiang.game.module.manage.service.impl;

import com.alibaba.fastjson2.JSON;
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.advertising.tencent.base.AdvertisingTencentServer;
import com.zanxiang.advertising.tencent.base.pojo.dto.CreateUserActionSetV3RpcDTO;
import com.zanxiang.advertising.tencent.base.pojo.vo.UserActionSetRpcVO;
import com.zanxiang.advertising.tencent.base.rpc.IAdAccountRpc;
import com.zanxiang.advertising.tencent.base.rpc.IUserActionSetRpc;
import com.zanxiang.advertising.tencent.base.rpc.IUserActionSetV3Rpc;
import com.zanxiang.erp.base.ErpServer;
import com.zanxiang.erp.base.pojo.vo.SysGameUserGroupRpcVO;
import com.zanxiang.erp.base.rpc.ISysGameUserGroupRpc;
import com.zanxiang.erp.base.rpc.ISysUserRpc;
import com.zanxiang.erp.security.util.SecurityUtil;
import com.zanxiang.game.back.base.ServerInfo;
import com.zanxiang.game.back.base.pojo.vo.GameBackPolicyRpcVO;
import com.zanxiang.game.back.base.rpc.IGameBackPolicyRpc;
import com.zanxiang.game.module.base.pojo.enums.AccountTypeEnum;
import com.zanxiang.game.module.base.pojo.enums.GameAuthEnum;
import com.zanxiang.game.module.manage.pojo.dto.AgentDTO;
import com.zanxiang.game.module.manage.pojo.dto.TencentNewWebUserActionSetDTO;
import com.zanxiang.game.module.manage.pojo.params.AgentAddParam;
import com.zanxiang.game.module.manage.pojo.params.AgentListParam;
import com.zanxiang.game.module.manage.pojo.vo.AgentAccountChoiceVO;
import com.zanxiang.game.module.manage.pojo.vo.AgentChoiceVO;
import com.zanxiang.game.module.manage.pojo.vo.AgentVO;
import com.zanxiang.game.module.manage.pojo.vo.GameInfoVO;
import com.zanxiang.game.module.manage.service.IAgentService;
import com.zanxiang.game.module.manage.service.IGameAuthRoleService;
import com.zanxiang.game.module.manage.service.IGameAuthService;
import com.zanxiang.game.module.manage.service.IGameService;
import com.zanxiang.game.module.mybatis.entity.Agent;
import com.zanxiang.game.module.mybatis.entity.GameAuthRole;
import com.zanxiang.game.module.mybatis.mapper.AgentMapper;
import com.zanxiang.module.util.JsonUtil;
import com.zanxiang.module.util.bean.BeanUtil;
import com.zanxiang.module.util.exception.BaseException;
import com.zanxiang.module.util.pojo.ResultVO;
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.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

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

/**
 * @author wcc
 */
@Slf4j
@Service
public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements IAgentService {

    @Autowired
    private IGameService gameService;

    @DubboReference(providedBy = AdvertisingTencentServer.SERVER_DUBBO_NAME)
    private IAdAccountRpc adAccountRpc;

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

    @DubboReference(providedBy = AdvertisingTencentServer.SERVER_DUBBO_NAME)
    private IUserActionSetV3Rpc userActionSetV3Rpc;

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

    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private IGameBackPolicyRpc gameBackPolicyRpc;

    @DubboReference(providedBy = ErpServer.SERVER_DUBBO_NAME)
    private ISysGameUserGroupRpc sysGameUserGroupRpc;

    @Autowired
    private IGameAuthRoleService gameAuthRoleService;

    @Autowired
    private IGameAuthService gameAuthService;

    @Override
    public IPage<AgentVO> listOfPage(AgentListParam param) {
        IPage<Agent> agentIPage = page(param.toPage(), new LambdaQueryWrapper<Agent>()
                .like(StringUtils.isNotEmpty(param.getAgentName()), Agent::getAgentName, param.getAgentName())
                .eq(StringUtils.isNoneBlank(param.getAgentKey()), Agent::getAgentKey, param.getAgentKey())
                .eq(param.getAccountType() != null, Agent::getAccountType, param.getAccountType())
                .eq(param.getAccountId() != null, Agent::getAccountId, param.getAccountId())
                .eq(param.getGameId() != null, Agent::getGameId, param.getGameId())
                .eq(param.getBackPolicyId() != null, Agent::getBackPolicyId, param.getBackPolicyId())
                .eq(param.getPutStatus() != null, Agent::getPutStatus, param.getPutStatus())
                .ge(param.getCreateBeginTime() != null, Agent::getCreateTime, param.getCreateBeginTime() == null ? null : LocalDateTime.of(param.getCreateBeginTime(), LocalTime.MIDNIGHT))
                .le(param.getCreateEndTime() != null, Agent::getCreateTime, param.getCreateEndTime() == null ? null : LocalDateTime.of(param.getCreateEndTime(), LocalTime.MIDNIGHT))
                .eq(Agent::getCreateBy, param.getPutUserId() == null ? SecurityUtil.getUserId() : param.getPutUserId())
                .orderByDesc(Agent::getCreateTime)
        );
        IPage<AgentVO> result = new Page<>(agentIPage.getCurrent(), agentIPage.getSize(), agentIPage.getTotal());
        if (CollectionUtils.isNotEmpty(agentIPage.getRecords())) {
            result.setRecords(toVOBatch(agentIPage.getRecords()));
        }
        return result;
    }

    @Override
    public Boolean newWebUserActionSet(TencentNewWebUserActionSetDTO dto) {
        ResultVO<Long> result = userActionSetV3Rpc.create(CreateUserActionSetV3RpcDTO.builder()
                .accountId(dto.getAdAccountId())
                .type("WEB")
                .name(dto.getName())
                .description(dto.getDescription())
                .enableConversionClaim(true)
                .unique(true)
                .build());
        if(result.isSuccess()) {
            return result.getData() != null;
        }
        throw new BaseException(result.getMsg());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean add(AgentAddParam dto) {
        AccountTypeEnum accountType = AccountTypeEnum.getByValue(dto.getAccountType());
        if (accountType != AccountTypeEnum.UN_PUT) {
            if (count(new LambdaQueryWrapper<Agent>()
                    .eq(Agent::getAccountType, dto.getAccountType())
                    .eq(Agent::getAccountId, dto.getAccountId())
            ) > 0) {
                throw new BaseException("一个账号只能建一个渠道，投一个游戏！！！");
            }
        }
        return save(Agent.builder()
                .agentName(dto.getAgentName())
                .agentKey(UUID.randomUUID().toString().replaceAll("-", ""))
                .accountType(dto.getAccountType())
                .accountId(dto.getAccountId())
                .gameId(dto.getGameId())
                .userActionSetId(dto.getUserActionSetId())
                .reportToken(dto.getReportToken())
                .reportUrl(dto.getReportUrl())
                .xgltDemandId(dto.getXgltDemandId())
                .backPolicyId(dto.getBackPolicyId())
                .putStatus(dto.getPutStatus())
                .createTime(LocalDateTime.now())
                .createBy(SecurityUtil.getUserId())
                .build()
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean configPutStatus(List<Long> agentIdList, Integer putStatus) {
        return update(new LambdaUpdateWrapper<Agent>()
                .set(Agent::getPutStatus, putStatus)
                .set(Agent::getUpdateBy, SecurityUtil.getUserId())
                .set(Agent::getUpdateTime, LocalDateTime.now())
                .in(Agent::getId, agentIdList)
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean configBackPolicy(List<Long> agentIdList, Long backPolicyId) {
        return update(new LambdaUpdateWrapper<Agent>()
                .set(Agent::getBackPolicyId, backPolicyId)
                .set(Agent::getUpdateBy, SecurityUtil.getUserId())
                .set(Agent::getUpdateTime, LocalDateTime.now())
                .in(Agent::getId, agentIdList)
        );
    }

    private List<AgentVO> toVOBatch(List<Agent> agentList) {
        if (CollectionUtils.isEmpty(agentList)) {
            return Collections.emptyList();
        }
        Set<Long> gameIds = new HashSet<>(agentList.size());
        Set<Long> txAccountIds = new HashSet<>(agentList.size());
        Set<Long> userIds = new HashSet<>(1);
        Set<Long> backPolicyIds = new HashSet<>();
        agentList.forEach(agent -> {
            AccountTypeEnum accountType = AccountTypeEnum.getByValue(agent.getAccountType());
            gameIds.add(agent.getGameId());
            if (AccountTypeEnum.TENCENT_H5 == accountType
                    || AccountTypeEnum.TENCENT_MINI_GAME == accountType) {
                txAccountIds.add(agent.getAccountId());
            }
            userIds.add(agent.getCreateBy());
            if (agent.getBackPolicyId() != null) {
                backPolicyIds.add(agent.getBackPolicyId());
            }
        });
        Map<String, UserActionSetRpcVO> userActionSetMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(txAccountIds)) {
            for (Long txAccountId : txAccountIds) {
                List<UserActionSetRpcVO> userActionSetList = userActionSetRpc.getByAccount(txAccountId).getData();
                if (CollectionUtils.isEmpty(userActionSetList)) {
                    continue;
                }
                userActionSetMap.putAll(userActionSetList.stream().collect(Collectors.toMap(obj -> obj.getAccountId() + "_" + obj.getUserActionSetId(), Function.identity())));
            }
        }
        Map<Long, GameBackPolicyRpcVO> backPolicyMap = new HashMap<>(0);
        if (CollectionUtils.isNotEmpty(backPolicyIds)) {
            backPolicyMap.putAll(gameBackPolicyRpc.listByIds(new ArrayList<>(backPolicyIds)).getData());
        }
        Map<Long, GameInfoVO> gameMap = gameService.infoByIds(gameIds).stream().collect(Collectors.toMap(GameInfoVO::getId, Function.identity()));
        Map<Long, String> userMap = sysUserRpc.getUserNameByIds(new ArrayList<>(userIds)).getData();
        return agentList.stream().map(agent -> {
            AgentVO vo = toVOSimple(agent);
            vo.setGameInfo(gameMap.get(agent.getGameId()));
            if (AccountTypeEnum.TENCENT_H5.getValue().equals(agent.getAccountType())) {
                vo.setUserActionSet(userActionSetMap.get(agent.getAccountId() + "_" + agent.getUserActionSetId()));
            }
            vo.setCreateByName(userMap.get(agent.getCreateBy()));
            if (agent.getBackPolicyId() != null) {
                vo.setBackPolicyInfo(backPolicyMap.get(agent.getBackPolicyId()));
            }
            return vo;
        }).collect(Collectors.toList());
    }

    private AgentVO toVOSimple(Agent agent) {
        AccountTypeEnum accountType = AccountTypeEnum.getByValue(agent.getAccountType());
        AgentVO vo = BeanUtil.copy(agent, AgentVO.class);
        if (accountType == AccountTypeEnum.BYTE) {
            vo.setAgentKey("?agentKey=" + agent.getAgentKey());
        } else if (accountType == AccountTypeEnum.TENCENT_H5 || accountType == AccountTypeEnum.TENCENT_MINI_GAME) {
            vo.setAgentKey("?state=" + agent.getAgentKey());
        } else {
            vo.setAgentKey("?agentKey=" + agent.getAgentKey());
        }
        return vo;
    }

    @Override
    public Tuple2<List<Long>, List<AgentDTO>> getUserAgent(String account, String pitcherId, Long agentId) {
        //判断是否是组长
        SysGameUserGroupRpcVO sysGameUserGroupRpcVO = sysGameUserGroupRpc.getByGroupUser(SecurityUtil.getCompanyId(),
                SecurityUtil.getUserId()).getData();
        //判断运营
        GameAuthRole gameAuthRole = gameAuthRoleService.getOne(new LambdaQueryWrapper<GameAuthRole>()
                .eq(GameAuthRole::getUserId, SecurityUtil.getUserId()));
        //分配的游戏不存在
        Tuple2<String, List<Long>> gameTuple = gameAuthService.getUserGameList(null);
        if (CollectionUtils.isEmpty(gameTuple.getT2())) {
            return Tuples.of(Collections.emptyList(), Collections.emptyList());
        }
        //自然量查询
        if (Objects.equals(agentId, Agent.DEFAULT_AGENT)) {
            //超管, 运营, 组长可以查询自然量
            if (SecurityUtil.isAdmin() || sysGameUserGroupRpcVO != null
                    || Objects.equals(gameAuthRole.getAuthType(), GameAuthEnum.MANAGE.getValue())
                    || Objects.equals(gameAuthRole.getAuthType(), GameAuthEnum.OPERATE.getValue())) {
                return Tuples.of(Collections.singletonList(Agent.DEFAULT_AGENT), Collections.emptyList());
            }
            //非管理员或者组长不允许查询自然量
            return Tuples.of(Collections.emptyList(), Collections.emptyList());
        }
        //渠道列表
        List<Agent> agentList;
        //是否需要自然量
        boolean defaultAgent = true;
        //超管/管理员权限, 返回所有渠道
        if (SecurityUtil.isAdmin() || Objects.equals(gameAuthRole.getAuthType(), GameAuthEnum.MANAGE.getValue())) {
            agentList = super.list(new LambdaQueryWrapper<Agent>()
                    .eq(Strings.isNotBlank(account), Agent::getAccountId, account)
                    .eq(Strings.isNotBlank(pitcherId), Agent::getCreateBy, pitcherId)
                    .eq(agentId != null, Agent::getId, agentId));
        } else if (Objects.equals(gameAuthRole.getAuthType(), GameAuthEnum.OPERATE.getValue())) {
            //运营权限, 获取拥有的游戏的所有渠道
            agentList = super.list(new LambdaQueryWrapper<Agent>()
                    .in(Agent::getGameId, gameTuple.getT2())
                    .eq(Strings.isNotBlank(account), Agent::getAccountId, account)
                    .eq(Strings.isNotBlank(pitcherId), Agent::getCreateBy, pitcherId)
                    .eq(agentId != null, Agent::getId, agentId));
        } else if (sysGameUserGroupRpcVO != null) {
            //组长权限, 获取组员id, 查询组内所有渠道
            Collection<Long> memberUserIdList = sysGameUserGroupRpc.memberUserIds(SecurityUtil.getCompanyId(),
                    SecurityUtil.getUserId()).getData();
            if (CollectionUtils.isEmpty(memberUserIdList)) {
                return Tuples.of(Collections.emptyList(), Collections.emptyList());
            }
            agentList = super.list(new LambdaQueryWrapper<Agent>()
                    .in(Agent::getGameId, gameTuple.getT2())
                    .eq(Strings.isNotBlank(account), Agent::getAccountId, account)
                    .eq(Strings.isNotBlank(pitcherId), Agent::getCreateBy, pitcherId)
                    .in(Agent::getCreateBy, memberUserIdList)
                    .eq(agentId != null, Agent::getId, agentId));
        } else {
            //组员, 只允许查看自己的数据
            agentList = super.list(new LambdaQueryWrapper<Agent>()
                    .in(Agent::getGameId, gameTuple.getT2())
                    .eq(Strings.isNotBlank(account), Agent::getAccountId, account)
                    .eq(Agent::getCreateBy, SecurityUtil.getUserId())
                    .eq(agentId != null, Agent::getId, agentId));
            defaultAgent = false;
        }
        //未指定搜索条件, 且可见自然量
        if (Strings.isBlank(account) && Strings.isBlank(pitcherId) && agentId == null && defaultAgent) {
            agentList.add(Agent.builder().id(Agent.DEFAULT_AGENT).agentName(Agent.DEFAULT_AGENT_NAME).build());
        }
        //设置归属投手
        List<Long> createByList = agentList.stream().map(Agent::getCreateBy).collect(Collectors.toList());
        Map<Long, String> userMap = sysUserRpc.getUserNameByIds(createByList).getData();
        List<AgentDTO> agentDTOList = agentList.stream().map(agent -> {
            AgentDTO agentDTO = BeanUtil.copy(agent, AgentDTO.class);
            agentDTO.setCreateByName(userMap.get(agent.getCreateBy()));
            return agentDTO;
        }).collect(Collectors.toList());
        //渠道id列表
        List<Long> agentIds = agentDTOList.stream().map(AgentDTO::getId).collect(Collectors.toList());
        //返回渠道列表
        return Tuples.of(agentIds, agentDTOList);
    }

    @Override
    public List<AgentAccountChoiceVO> agentAccountChoiceList() {
        return this.getUserAgentList().stream()
                .map(agent -> BeanUtil.copy(agent, AgentAccountChoiceVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public List<AgentChoiceVO> agentChoiceList() {
        return this.getUserAgentList().stream()
                .map(agent -> BeanUtil.copy(agent, AgentChoiceVO.class))
                .collect(Collectors.toList());
    }

    private List<Agent> getUserAgentList() {
        //判断是否是组长
        SysGameUserGroupRpcVO sysGameUserGroupRpcVO = sysGameUserGroupRpc.getByGroupUser(SecurityUtil.getCompanyId(),
                SecurityUtil.getUserId()).getData();
        //判断运营
        GameAuthRole gameAuthRole = gameAuthRoleService.getOne(new LambdaQueryWrapper<GameAuthRole>()
                .eq(GameAuthRole::getUserId, SecurityUtil.getUserId()));
        //渠道列表
        List<Agent> agentList = new ArrayList<>();
        //是否需要自然量
        boolean defaultAgent = true;
        //超管权限, 返回所有渠道
        if (SecurityUtil.isAdmin() || Objects.equals(gameAuthRole.getAuthType(), GameAuthEnum.MANAGE.getValue())) {
            agentList = super.list();
        } else if (Objects.equals(gameAuthRole.getAuthType(), GameAuthEnum.OPERATE.getValue())) {
            //运营权限, 获取拥有的游戏的所有渠道
            Tuple2<String, List<Long>> tuple2 = gameAuthService.getUserGameList(null);
            if (CollectionUtils.isNotEmpty(tuple2.getT2())) {
                agentList = super.list(new LambdaQueryWrapper<Agent>().in(Agent::getGameId, tuple2.getT2()));
            }
        } else if (sysGameUserGroupRpcVO != null) {
            //组长权限, 获取组员id, 查询组内所有渠道
            Collection<Long> memberUserIdList = sysGameUserGroupRpc.memberUserIds(SecurityUtil.getCompanyId(),
                    SecurityUtil.getUserId()).getData();
            agentList = super.list(new LambdaQueryWrapper<Agent>()
                    .in(!SecurityUtil.isAdmin(), Agent::getCreateBy, memberUserIdList));
        } else {
            //组员权限, 组员返回自己拥有的渠道
            agentList = super.list(new LambdaQueryWrapper<Agent>()
                    .eq(!SecurityUtil.isAdmin(), Agent::getCreateBy, SecurityUtil.getUserId()));
            defaultAgent = false;
        }
        //超管, 组长, 运营权限可以存在自然量搜索
        if (defaultAgent) {
            agentList.add(Agent.builder().id(Agent.DEFAULT_AGENT).agentName(Agent.DEFAULT_AGENT_NAME).build());
        }
        return agentList;
    }

    @Override
    public AgentDTO getAgentById(long agentId) {
        if (Objects.equals(agentId, Agent.DEFAULT_AGENT)) {
            return null;
        }
        Agent agent = super.getById(agentId);
        Map<Long, String> userMap = sysUserRpc.getUserNameByIds(Collections.singletonList(agent.getCreateBy())).getData();
        AgentDTO agentDTO = BeanUtil.copy(agent, AgentDTO.class);
        agentDTO.setCreateByName(userMap.get(agent.getCreateBy()));
        return agentDTO;
    }

    @Override
    public Map<String, String> channelTransform(String channel) {
        if (Strings.isBlank(channel) || Strings.isBlank(channel.trim())) {
            return Collections.emptyMap();
        }
        //判断是否是json, 是就直接转成map
        if (this.isJsonStr(channel)) {
            return JsonUtil.toMap(channel, Map.class, String.class);
        }
        //非json, 自己解析
        Map<String, String> paramMap = new HashMap<>(11);
        String[] params = channel.split("&");
        for (String param : params) {
            String[] keyValue = param.split("=");
            if (keyValue.length > 1) {
                String key = keyValue[0];
                String value = keyValue[1];
                paramMap.put(key, value);
            }
        }
        return paramMap;
    }

    private boolean isJsonStr(String str) {
        boolean result = false;
        try {
            JSON.parse(str);
            result = true;
        } catch (Exception ignored) {
        }
        return result;
    }
}
