package cn.reghao.autodop.dagent.rpc;

import cn.reghao.autodop.common.mqtt.DefaultMqttClient;
import cn.reghao.autodop.common.msg.Message;
import cn.reghao.autodop.common.msg.MsgQueue;
import cn.reghao.autodop.common.msg.rpc.RpcPayload;
import cn.reghao.autodop.common.msg.rpc.RpcMsg;
import cn.reghao.autodop.common.msg.rpc.RpcResult;
import cn.reghao.autodop.common.msg.rpc.clazz.RpcClazz;
import cn.reghao.autodop.dagent.rpc.impl.AppClazzRpcImpl;
import cn.reghao.autodop.dagent.rpc.impl.DockerClazzRpcImpl;
import cn.reghao.autodop.dagent.rpc.impl.MachineClazzRpcImpl;
import cn.reghao.jdkutil.serializer.JsonConverter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.stereotype.Component;

import java.lang.management.ManagementFactory;

/**
 * 处理 sub.{nodeId}.dagent topic 中的消息
 *
 * @author reghao
 * @date 2021-05-24 09:24:03
 */
@Slf4j
@Component
public class RpcListener implements MqttCallback {
    private final long startTime;
    private final DefaultMqttClient mqttClient;
    private final AppClazzRpcImpl appClazz;
    private final MachineClazzRpcImpl machineClazz;
    private final DockerClazzRpcImpl dockerClazz;

    public RpcListener(DefaultMqttClient mqttClient, AppClazzRpcImpl appClazz, MachineClazzRpcImpl machineClazz,
                       DockerClazzRpcImpl dockerClazz) {
        this.startTime = ManagementFactory.getRuntimeMXBean().getStartTime();
        this.mqttClient = mqttClient;
        this.appClazz = appClazz;
        this.machineClazz = machineClazz;
        this.dockerClazz = dockerClazz;
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) {
        try {
            String msg = message.toString();
            Message callMessage = JsonConverter.jsonToObject(msg, Message.class);
            RpcMsg callMsg = callMessage.getRpcMsg();

            RpcPayload rpcPayload = callMsg.getRpcPayload();
            String clazz = rpcPayload.getClazz();
            RpcResult rpcResult;
            switch (RpcClazz.valueOf(clazz)) {
                case AppRpcClazz:
                    rpcResult = appClazz.process(rpcPayload);
                    break;
                case MachineRpcClazz:
                    rpcResult = machineClazz.process(rpcPayload);
                    break;
                case DockerRpcClazz:
                    rpcResult = dockerClazz.process(rpcPayload);
                    break;
                default:
                    String err = String.format("找不到 %s 类型", clazz);
                    rpcResult = RpcResult.error(err);
            }

            RpcMsg resultMsg = RpcMsg.resultMsg(callMsg, rpcResult);
            Message message1 = Message.rpcMessage(resultMsg);
            rpcReply(message1);
        } catch (Exception e) {
            log.error("MQTT message exception -> {}", e.getMessage());
        }
    }

    private void rpcReply(Message message) throws MqttException {
        String topic = MsgQueue.dmasterTopic();
        mqttClient.pub(topic, 1, JsonConverter.objectToJson(message));
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        if (!token.isComplete()) {
            log.error("token -> {}", token);
        } else {
            // 由于使用 MQTT 记录日志，而每条消息发送成功后都会调用此方法，因此会形成循环引用，导致超高 CPU 使用率
            // log.info("token -> {}", token);
        }
    }

    @Override
    public void connectionLost(Throwable cause) {
        log.error("MQTT connection lost -> {}", cause.getMessage());
    }
}
