package cn.iocoder.yudao.module.product.emqx;

import com.alibaba.cloud.commons.lang.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

@Slf4j
public class DeviceMqttClient {

    private static final int MAX_INF_LIGHT = 100000;
    private final MqttProperties config;
    private MqttClient client;
    private MqttConnectOptions options;
    private boolean connecting = false;

    public DeviceMqttClient(MqttProperties config) {
        this.config = config;
    }

    /**
     * 判断MqttClient是否正常连接
     * @return 客户端是否连接
     */
    public boolean isConnected() {
        return client != null && client.isConnected();
    }

    /**
     * 用来连接服务器
     */
    public void connect() throws Exception {
        if (client != null && (client.isConnected() || connecting)) {
            log.debug("尝试MQTT连接时发现MQTTClient处于连接状态");
            return;
        }
        log.debug("尝试MQTT连接，client = {}，isConnected={}, connecting={}", client, isConnected(), connecting);
        connecting = true;

        if (client == null) {
            // MemoryPersistence设置clientid的保存形式，默认为以内存保存;
            client = new MqttClient(config.getHostUrl(), config.getClientId(), new MemoryPersistence());

            // 设置回调处理
            client.setCallback(new DeviceMqttCallback(this));
        }
        if (options == null) {
            options = new MqttConnectOptions();
            options.setUserName(config.getUsername());
            options.setPassword(config.getPassword().toCharArray());
            options.setMaxInflight(MAX_INF_LIGHT);
            options.setCleanSession(config.getCleanSession());
            // 设置超时时间
            options.setConnectionTimeout(config.getTimeout());
            // 设置会话心跳时间
            options.setKeepAliveInterval(config.getKeepAlive());
            // 设置掉线自动重连（如果有订阅消息，自动重连要设置为false，否则不会自动订阅）
            options.setAutomaticReconnect(config.getReconnect());  // 重要
        }

        try {
            client.connect(options);
            // 这里一直保持链接的是传感器数据的主题
            client.subscribe(config.getSensorTopic(), config.getQos());

//            log.info("MQTT服务器连接成功！");
            connecting = false;
        } catch (Exception ex) {
            connecting = false;
            log.error("连接MQTT服务失败，serverUri={}, err={}", client.getServerURI(), ex.getMessage(), ex);
            throw ex;
        } finally {
            connecting = false;
        }
    }

    public void sendMessage(String topic, byte[] msg) {
        if (StringUtils.isBlank(topic)) {
            return;
        }
        if (msg == null || msg.length < 1) {
            return;
        }
        sendMessage(this.getTopic(topic), msg);
    }

    public void sendMessage(String topic, String msg) {
        if (StringUtils.isBlank(topic)) {
            return;
        }
        if (msg == null || msg.equals("")) {
            return;
        }
        sendMessage(this.getTopic(topic), msg);
    }

    private void sendMessage(MqttTopic topic, String msg) {
        if (topic == null) {
            return;  //  || StringUtils.isBlank(msg)
        }
        try {
            topic.publish(msg == null ? new byte[0] : msg.getBytes(), 2, true);
            log.info("设备实时数据发送成功，topic={}, clientId={}, msg={}", topic.getName(), this.client.getClientId(), msg);
        } catch (Exception ex) {
            log.error("设备实时数据发送失败，topic={}, clientId={}, ex ={}", topic.getName(), this.client.getClientId(), ex);
        }
    }

    private void sendMessage(MqttTopic topic, byte[] msg) {
        if (topic == null) {
            return;  //  || StringUtils.isBlank(msg)
        }
        try {
            topic.publish(msg, 2, true);
            log.info("设备实时数据发送成功，topic={}, clientId={}", topic.getName(), this.client.getClientId());
        } catch (Exception ex) {
            log.error("是被实时数据发送失败，topic={}, clientId={}, ex = {}", topic.getName(), this.client.getClientId(), ex);
        }
    }

    /**
     * 清除主题的保留消息
     * 当 publish 方法的retained 参数为false时不需要调
     * 用该方法，不然会造成无限的空数据导致数据传输越来越慢
     * @param topic 主题
     */
    public void clearRetained(String topic) {
        try {
            MqttTopic mqttTopic = getTopic(topic);
            mqttTopic.publish(new byte[0], 0, true);
        } catch (Exception ignore) {
        }
    }

    /**
     * 获取消息主题
     *
     * @param topic 主题
     * @return 消息主题
     */
    private MqttTopic getTopic(String topic) {
        if (client == null || !client.isConnected()) {
            try {
                this.connect();
            } catch (Exception ignore) {
            }
        }
        if (client == null) {
            throw new RuntimeException("MQTT服务器启动失败！");
        }
        return client.getTopic(topic);
    }
}
