package cn.iocoder.yudao.module.iot.framework.mqtt.config;

import cn.iocoder.yudao.module.iot.framework.mqtt.annotation.Topic;
import cn.iocoder.yudao.module.iot.framework.mqtt.common.MqttCallbackImpl;
import cn.iocoder.yudao.module.iot.framework.mqtt.entity.Pattern;
import cn.iocoder.yudao.module.iot.framework.mqtt.entity.SubscriptTopic;
import cn.iocoder.yudao.module.iot.framework.mqtt.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * mqtt配置类
 *
 * @author jie
 */
@Slf4j
@Configuration
@EnableConfigurationProperties(DefaultEmqProperties.class)
public class DefaultEmqConfig {
    /**
     * MQTT的连接设置
     *
     * @param emqProperties
     * @return
     */
    @Bean
    public MqttConnectOptions getOption(DefaultEmqProperties emqProperties) {
        MqttConnectOptions options = new MqttConnectOptions();
        emqProperties.setClientId("clientId" + String.valueOf(DateUtils.getMillsLong()));
        log.info("------mqtt clientid =" + emqProperties.getClientId());
        options.setUserName(emqProperties.getUserName());
        options.setPassword(emqProperties.getPassword().toCharArray());
        // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录，这里设置为true表示每次连接到服务器都以新的身份连接
        options.setCleanSession(emqProperties.getCleanSession());
        //断线重连
        options.setAutomaticReconnect(emqProperties.getReconnect());
        // 设置超时时间 单位为秒
        options.setConnectionTimeout(emqProperties.getTimeout());
        // 设置会话心跳时间 单位为秒 服务器会每隔1.5*10秒的时间向客户端发送个消息判断客户端是否在线，但这个方法并没有重连的机制
        options.setKeepAliveInterval(emqProperties.getKeepAlive());
        return options;
    }

    @Bean
    public DefaultBizTopicSet defaultBizTopicSet(ApplicationContext applicationContext) {
        List<SubscriptTopic> topicMap = new ArrayList<>();
        //得到所有使用@Topic注解的类
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Topic.class);
        for (String className : beansWithAnnotation.keySet()) {
            Class<?> classByteCode = beansWithAnnotation.get(className).getClass();
            //获取类的注解属性
            Topic annotation = AnnotationUtils.findAnnotation(classByteCode, Topic.class);
            String topic = annotation.topic();
            int qos = annotation.qos();
            Pattern patten = annotation.patten();
            String group = annotation.group();
            String subTopic = topic;
            if (patten == Pattern.SHARE) {
                subTopic = group + "/" + topic;
            } else if (patten == Pattern.QUEUE) {
                subTopic = topic;
            }
            topicMap.add(new SubscriptTopic(topic, subTopic, patten, qos, (IMqttMessageListener) applicationContext.getBean(classByteCode)));
        }

        return new DefaultBizTopicSet(topicMap);
    }

    /**
     * 系统默认的MQTT连接
     *
     * @param options            MQTT连接参数选项
     * @param emqProperties      MQTT连接配置
     * @param applicationContext 系统上下文
     * @return
     * @throws Exception 异常
     */
    @Bean
    public MqttClient mqttClient(MqttConnectOptions options, DefaultEmqProperties emqProperties, DefaultBizTopicSet defaultBizTopicSet, ApplicationContext applicationContext) throws Exception {
        MqttClient mqttClient = new MqttClient(emqProperties.getBroker(), emqProperties.getClientId(), new MemoryPersistence());
        mqttClient.setCallback(new MqttCallbackImpl(defaultBizTopicSet.getTopicMap(), mqttClient, options));
        return mqttClient;
    }
    public static Boolean isEnable(DefaultEmqProperties emqProperties){
        return emqProperties.getIsEnable();
    }
}
