package com.njzscloud.common.mqtt.support;

import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.jackson.Jackson;
import com.njzscloud.common.mqtt.config.MqttProperties;
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.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

@Slf4j
public class MqttCliWrapper implements BeanPostProcessor {
    static final Map<String, Consumer<MqttMsg>> fn = new ConcurrentHashMap<>();
    private MqttClient client;

    public MqttCliWrapper(MqttProperties mqttProperties) {
        String broker = mqttProperties.getBroker();
        String clientId = mqttProperties.getClientId() + "_" + new Date().getTime();
        String username = mqttProperties.getUsername();
        String password = mqttProperties.getPassword();
        try {
            MqttDefaultFilePersistence persistence = new MqttDefaultFilePersistence("logs/mqtt");
            client = new MqttClient(broker, clientId, persistence);
            MqttConnectOptions options = new MqttConnectOptions();
            options.setAutomaticReconnect(true);
            options.setConnectionTimeout(5000);
            options.setMaxReconnectDelay(30000);
            if (StrUtil.isNotBlank(username) && StrUtil.isNotBlank(password)) {
                options.setUserName(username);
                options.setPassword(password.toCharArray());
            }

            client.connect(options);
            client.setCallback(new MsgHandler(fn));
        } catch (Exception e) {
            client = null;
            log.error("mqtt连接失败", e);
        }
    }

    public void shutdown() {
        try {
            if (client != null) {
                client.disconnect();
            }
        } catch (Exception e) {
            log.error("mqtt关闭失败", e);
        }
    }

    public void subscribe(String topic, int qos) {
        try {
            client.subscribe(topic, qos);
            log.info("mqtt 订阅消息：{} {}", topic, qos);
        } catch (Exception e) {
            log.error("mqtt 订阅失败：{} {}", topic, qos, e);
        }
    }

    public void subscribe(String topic) {
        this.subscribe(topic, 0);
    }

    public void subscribe(String topic, int qos, Consumer<MqttMsg> handler) {
        fn.put(topic, handler);
        this.subscribe(topic, qos);
    }

    public void subscribe(String topic, Consumer<MqttMsg> handler) {
        this.subscribe(topic, 0, handler);
    }

    public void publish(String topic, int qos, Object msg) {
        String jsonStr = null;
        try {
            jsonStr = Jackson.toJsonStr(msg);
            // log.info("mqtt 发布消息：{} {} {}", topic, qos, jsonStr);
            MqttMessage message = new MqttMessage(jsonStr.getBytes());
            message.setQos(qos);
            client.publish(topic, message);
        } catch (Exception e) {
            log.error("mqtt 发布消息失败：{} {} {}", topic, qos, jsonStr, e);
        }
    }

    public void publish(String topic, Object msg) {
        this.publish(topic, 0, msg);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> clazz = bean.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            MqttListener mqttListener = method.getAnnotation(MqttListener.class);
            if (mqttListener == null) continue;
            String topic = mqttListener.topic();
            int qos = mqttListener.qos();
            this.subscribe(topic, qos);
            Consumer<MqttMsg> handler = (msg) -> {
                try {
                    method.invoke(bean, msg);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            };
            fn.put(topic, handler);
        }
        return bean;
    }

    public void unsubscribe(String topic) {
        try {
            client.unsubscribe(topic);
        } catch (Exception e) {
            log.error("mqtt 取消订阅失败：{}", topic, e);
        }
    }


}
