package io.wdd.rpc.message.handler;

import io.wdd.rpc.message.OctopusMessage;
import io.wdd.rpc.message.OctopusMessageType;
import io.wdd.server.config.ServerCommonPool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static io.wdd.rpc.message.handler.OMessageToServerListener.FROM_AGENT_MATCH_TO_AGENT_MAP;
import static io.wdd.rpc.message.handler.OMessageToServerListener.OCTOPUS_MESSAGE_FROM_AGENT;

@Service
@Slf4j(topic = "Octopus Message Handler")
public class OMessageHandler {


    /**
     * 创建 发送和接收 OctopusMessage之间的比对关系
     *
     * @param messageType
     * @param messageInitTime 必须使用 TimeUtils.currentFormatTime();
     * @return
     */
    public static String GenerateOMessageMatchKey(OctopusMessageType messageType, LocalDateTime messageInitTime) {

        String relayMatchKey = messageType.toString() + messageInitTime.toString();

        return relayMatchKey;
    }

    @PostConstruct
    public void daemonHandleReplayOMFromAgent() {

        // 异步任务启动
        CompletableFuture.runAsync(
                () -> doHandleOMessageFromAgent(),
                ServerCommonPool.pool
        );

    }

    public static OMessageReplayContent WaitFromAgent(OctopusMessage originOMessage, int waitFroReplayNum) {

        // 构建 MatchKey
        String matchKey = GenerateOMessageMatchKey(
                originOMessage.getOctopusMessageType(),
                originOMessage.getInit_time()
        );

        // 构造等待对象
        OMessageReplayContent replayContent = OMessageReplayContent.build(
                originOMessage,
                waitFroReplayNum
        );

        // 开始等待
        FROM_AGENT_MATCH_TO_AGENT_MAP.put(
                matchKey,
                replayContent
        );

        //debug
        log.debug(
                "wait from agent map is => {}",
                FROM_AGENT_MATCH_TO_AGENT_MAP
        );


        return replayContent;
    }

    public static void StopWaitingResult(OctopusMessage octopusMessage) {

        // 构建 MatchKey
        String matchKey = GenerateOMessageMatchKey(
                octopusMessage.getOctopusMessageType(),
                octopusMessage.getInit_time()
        );

        // 开始等待
        if (FROM_AGENT_MATCH_TO_AGENT_MAP.containsKey(matchKey)) {
            FROM_AGENT_MATCH_TO_AGENT_MAP.remove(matchKey);
        }

    }

    /**
     * 解析所有从Agent传回的消息，中央集中化处理
     */
    private void doHandleOMessageFromAgent() {

        // 死循环,不断的轮询  OCTOPUS_MESSAGE_FROM_AGENT
        while (true) {

            if (OCTOPUS_MESSAGE_FROM_AGENT.isEmpty()) {

                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // 返回,继续死循环
                continue;
            }

            // 拿到消息
            OctopusMessage replayOMessage = OCTOPUS_MESSAGE_FROM_AGENT.poll();

            // 构造 replayMatchKey
            String matchKey = GenerateOMessageMatchKey(
                    replayOMessage.getOctopusMessageType(),
                    replayOMessage.getInit_time()
            );

            log.info(
                    "接收到的 matchKey为 {}, 内容为 {}",
                    matchKey,
                    replayOMessage
            );

            if (!FROM_AGENT_MATCH_TO_AGENT_MAP.containsKey(matchKey)) {
                // 没有这个Key,说明等待结果已经超时了,直接丢弃,然后继续循环
                // todo 错误的数据需要放置于某处
                log.warn(
                        "等待队列里面没有该回复的结果key =>",
                        matchKey
                );

                continue;
            }

            // 归还信息
            // 拿到原始信息
            OMessageReplayContent oMessageReplayContent = FROM_AGENT_MATCH_TO_AGENT_MAP.get(matchKey);
            OctopusMessage originOMessage = oMessageReplayContent.getOriginOMessage();
            originOMessage.setResultCode(replayOMessage.getResultCode());
            originOMessage.setResult(replayOMessage.getResult());

            // 通知等待线程
            oMessageReplayContent
                    .getCountDownLatch()
                    .countDown();
        }

    }
}
