package com.zanxiang.game.back.serve.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.zanxiang.erp.base.ErpServer;
import com.zanxiang.erp.base.rpc.IDingTalkMsgRpc;
import com.zanxiang.game.back.serve.dao.mapper.GameTencentMiniGameCallbackMapper;
import com.zanxiang.game.back.serve.pojo.entity.GameTencentMiniGameCallback;
import com.zanxiang.game.back.serve.pojo.entity.GameTencentMiniGameRoleRegister;
import com.zanxiang.game.back.serve.pojo.entity.GameTencentMiniGameUser;
import com.zanxiang.game.back.serve.pojo.enums.BackStatusEnum;
import com.zanxiang.game.back.serve.pojo.enums.BackTypeEnum;
import com.zanxiang.game.back.serve.service.IGameTencentMiniGameCallbackService;
import com.zanxiang.game.back.serve.service.IGameTencentMiniGameRoleRegisterService;
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.DateUtil;
import com.zanxiang.module.util.JsonUtil;
import com.zanxiang.module.util.URIUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GameTencentMiniGameCallbackServiceImpl extends ServiceImpl<GameTencentMiniGameCallbackMapper, GameTencentMiniGameCallback>
        implements IGameTencentMiniGameCallbackService {

    @Value("${spring.kafka.tencentCallbackTopic}")
    private String tencentCallbackTopic;

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

    @DubboReference(providedBy = ErpServer.SERVER_DUBBO_NAME)
    private IDingTalkMsgRpc dingTalkMsgRpc;

    @Autowired
    private KafkaProducer<String, String> kafkaProducer;

    @Lazy
    @Autowired
    private IGameTencentMiniGameUserService gameTencentMiniGameUserService;

    @Lazy
    @Autowired
    private IGameTencentMiniGameRoleRegisterService gameTencentMiniGameRoleRegisterService;

    private static final ThreadPoolExecutor THREAD_POOL_CALL_BACK_URL_UPDATE = new ThreadPoolExecutor(
            10,
            10,
            0,
            TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(),
            new ThreadFactoryBuilder()
                    .setNameFormat("sync-ipDataAssay-%d").build());

    @Override
    public boolean callback(Map<String, Object> paramMap) {
        GameTencentMiniGameCallback callback = null;
        try {
            callback = JsonUtil.toObj(JsonUtil.toString(paramMap), GameTencentMiniGameCallback.class);
            AgentRpcVO agent = agentRpc.getByTencentAccountId(callback.getAccountId()).getData();
            if (agent != null) {
                callback.setAgentKey(agent.getAgentKey());
                callback.setGameId(agent.getGameId());
                callback.setWechatAppId(agent.getAppId());
            } else {
                callback.setAgentKey("-");
                callback.setGameId(-1L);
                log.error("腾讯检测链接数据找不到渠道, callback ：{}, paramMap : {}", JsonUtil.toString(callback), JsonUtil.toString(paramMap));
            }
            callback.setDay(callback.getClickTime() == null || callback.getClickTime() < 1000 ? LocalDate.now() : DateUtil.secondToLocalDate(callback.getClickTime()));
            save(callback);
        } catch (Exception e) {
            log.error("接收到腾讯监测链接数据保存异常, paramMap : {}, errorMsg : {}", JsonUtil.toString(paramMap), e.getMessage(), e);
            //发送钉钉消息通知开发
            dingTalkMsgRpc.sendByUserId(220L, DateUtil.formatLocalDateTime(LocalDateTime.now()) + " : 腾讯小游戏监测链接数据入库异常");
        }
        if (callback != null) {
            try {
                kafkaProducer.send(new ProducerRecord<>(tencentCallbackTopic, callback.getAccountId().toString(), JsonUtil.toString(callback)));
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        return true;
    }

    @Override
    public String getCallBackUrl(Long gameId, String wechatOpenid, String clickId, LocalDateTime registerTime, boolean matchTime) {
        try {
            //1. 精准匹配, openId 必须匹配, requestId, clickId, impressionId, 有一个匹配上即可
            GameTencentMiniGameCallback tencentMiniGameCallback = super.getOne(
                    new LambdaQueryWrapper<GameTencentMiniGameCallback>()
                            .eq(GameTencentMiniGameCallback::getGameId, gameId)
                            .eq(GameTencentMiniGameCallback::getWechatOpenid, wechatOpenid)
                            .and(qw -> qw.eq(GameTencentMiniGameCallback::getImpressionId, clickId)
                                    .or().eq(GameTencentMiniGameCallback::getRequestId, clickId)
                                    .or().eq(GameTencentMiniGameCallback::getClickId, clickId))
                            .orderByDesc(GameTencentMiniGameCallback::getClickTime)
                            .last("limit 1"));
            //2. 模糊匹配, 分别使用 requestId, clickId, impressionId 匹配, 有一个匹配上即可, 加上注册时间距离最近条件
            if (tencentMiniGameCallback == null) {
                tencentMiniGameCallback = super.getOne(new LambdaQueryWrapper<GameTencentMiniGameCallback>()
                        .eq(GameTencentMiniGameCallback::getGameId, gameId)
                        .and(qw -> qw.eq(GameTencentMiniGameCallback::getImpressionId, clickId)
                                .or().eq(GameTencentMiniGameCallback::getRequestId, clickId)
                                .or().eq(GameTencentMiniGameCallback::getClickId, clickId))
                        .le(matchTime, GameTencentMiniGameCallback::getClickTime, DateUtil.localDateTimeToSecond(registerTime))
                        .orderByDesc(GameTencentMiniGameCallback::getClickTime)
                        .last("limit 1"));
            }
            //3. 兜底逻辑, 只用openId匹配
            if (tencentMiniGameCallback == null) {
                tencentMiniGameCallback = super.getOne(new LambdaQueryWrapper<GameTencentMiniGameCallback>()
                        .eq(GameTencentMiniGameCallback::getGameId, gameId)
                        .eq(GameTencentMiniGameCallback::getWechatOpenid, wechatOpenid)
                        .orderByDesc(GameTencentMiniGameCallback::getClickTime)
                        .last("limit 1"));
            }
            //判断监测链接数据
            String callBack = Optional.ofNullable(tencentMiniGameCallback)
                    .map(GameTencentMiniGameCallback::getCallback)
                    .orElse(null);
            if (Strings.isBlank(callBack)) {
                log.error("匹配监测链接为空, gameId : {}, wechatOpenid : {}, clickId : {}", gameId, wechatOpenid, clickId);
                return null;
            }
            //返回匹配回传地址
            return URIUtil.decodeURIComponent(callBack);
        } catch (Exception e) {
            log.error("匹配监测链接异常, gameId : {}, wechatOpenid : {}, clickId : {}", gameId, wechatOpenid, clickId);
        }
        return null;
    }

    @Override
    public void delayUpdateMiniGameUserCallBackUrl(Long gameId, LocalDateTime startTime) {
        //时间范围
        LocalDateTime minCreateTime = startTime == null ? LocalDateTime.now().minusHours(1) : startTime;
        //查询5分钟之内, callBackUrl未匹配上的数据
        List<GameTencentMiniGameUser> miniGameUserList = gameTencentMiniGameUserService.list(
                new LambdaQueryWrapper<GameTencentMiniGameUser>()
                        .eq(GameTencentMiniGameUser::getGameId, gameId)
                        .isNull(GameTencentMiniGameUser::getCallBackUrl)
                        .gt(GameTencentMiniGameUser::getCreateTime, minCreateTime)
                        .eq(GameTencentMiniGameUser::getBackType, BackTypeEnum.BACK_API.getBackType()));
        if (CollectionUtils.isEmpty(miniGameUserList)) {
            return;
        }
        //数据处理
        List<CompletableFuture<?>> futures = miniGameUserList.stream().map(user -> CompletableFuture.runAsync(() -> {
            try {
                this.miniGameUserUpdate(user);
            } catch (Exception e) {
                log.error("定时器-腾讯小游戏村注册检测异常, id : {}, e : {}", user.getId(), e.getMessage(), e);
            }
        }, THREAD_POOL_CALL_BACK_URL_UPDATE)).collect(Collectors.toList());
        try {
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
        } catch (Exception e) {
            log.error("定时器-腾讯小游戏村注册检测等待所有队列消息消费完成时出现异常: {}", e.getMessage());
        }
    }

    private void miniGameUserUpdate(GameTencentMiniGameUser miniGameUser) {
        //匹配回传URL
        String callBackUrl = this.getCallBackUrl(miniGameUser.getGameId(),
                miniGameUser.getWechatOpenid(), miniGameUser.getClickId(), miniGameUser.getRegisterTime(), false);
        if (Strings.isBlank(callBackUrl)) {
            return;
        }
        //设置回传地址
        miniGameUser.setCallBackUrl(callBackUrl);
        //数据修正
        gameTencentMiniGameUserService.updateById(miniGameUser);
        //判断是否已经执行回传异常
        if (Objects.equals(miniGameUser.getBackStatus(), BackStatusEnum.FAILED.getBackStatus())) {
            //已执行的重新补回传
            gameTencentMiniGameUserService.userBack(miniGameUser);
        }
        //查询相关创角回传是否存在
        List<GameTencentMiniGameRoleRegister> miniGameRoleRegisterList = gameTencentMiniGameRoleRegisterService.list(
                new LambdaQueryWrapper<GameTencentMiniGameRoleRegister>()
                        .eq(GameTencentMiniGameRoleRegister::getGameId, miniGameUser.getGameId())
                        .eq(GameTencentMiniGameRoleRegister::getWechatAppId, miniGameUser.getWechatAppId())
                        .eq(GameTencentMiniGameRoleRegister::getWechatOpenid, miniGameUser.getWechatOpenid())
                        .eq(GameTencentMiniGameRoleRegister::getAdAccountId, miniGameUser.getAdAccountId())
                        .eq(GameTencentMiniGameRoleRegister::getBackStatus, BackStatusEnum.FAILED.getBackStatus())
                        .eq(GameTencentMiniGameRoleRegister::getBackType, BackTypeEnum.BACK_API.getBackType()));
        if (CollectionUtils.isEmpty(miniGameRoleRegisterList)) {
            return;
        }
        //注册回传补充
        miniGameRoleRegisterList.forEach(miniGameRole -> {
            try {
                gameTencentMiniGameRoleRegisterService.roleRegisterBack(miniGameRole);
            } catch (Exception e) {
                log.error("定时器-腾讯小游戏村创角补回传异常, id : {}, e : {}", miniGameRole.getId(), e.getMessage(), e);
            }
        });
    }

    @Override
    public Map<String, String> getCallBackByOpenId(Long gameId, String wechatOpenid) {
        //查询监测链接
        GameTencentMiniGameCallback tencentMiniGameCallback = super.getOne(
                new LambdaQueryWrapper<GameTencentMiniGameCallback>()
                        .eq(GameTencentMiniGameCallback::getGameId, gameId)
                        .eq(GameTencentMiniGameCallback::getWechatOpenid, wechatOpenid)
                        .orderByDesc(GameTencentMiniGameCallback::getClickTime)
                        .last("limit 1"));
        //没有匹配上
        if (tencentMiniGameCallback == null) {
            return null;
        }
        Map<String, String> map = new HashMap<>(5);
        map.put("id", tencentMiniGameCallback.getId().toString());
        map.put("gameId", tencentMiniGameCallback.getGameId().toString());
        map.put("accountId", tencentMiniGameCallback.getAccountId().toString());
        map.put("agentKey", tencentMiniGameCallback.getAgentKey());
        map.put("adgroupId", tencentMiniGameCallback.getAdgroupId().toString());
        map.put("impressionId", tencentMiniGameCallback.getImpressionId());
        return map;
    }
}
