package com.usky.cdi.service.mqtt;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * @author fyc
 * @email yuchuan.fu@chinausky.com
 * @date 2025/12/22
 * 动态 MQTT 连接工具类
 * 用法：注入后调用 connectOrRefresh(...) 即可
 *
 */

@Slf4j
@Component
@RequiredArgsConstructor
public class MqttConnectionTool {

    private final GenericApplicationContext context;

    /* 默认 topic，可外部再 set */
    private String defaultTopic = "testTopic";

    /* 默认 keep-alive，可外部再 set */
    private int keepAlive = 60;

    private static final String MQTT_URL = "ssl://114.80.201.143:8883";

    /**
     * 存储每个用户名对应的连接信息
     */
    private final Map<String, ConnectionInfo> connectionMap = new ConcurrentHashMap<>();

    /**
     * 连接信息内部类
     */
    private static class ConnectionInfo {
        private final String handlerBeanName;
        private final String consumerBeanName;
        private final String factoryBeanName;
        private final String gatewayBeanName;

        public ConnectionInfo(String username) {
            this.handlerBeanName = "mqttHandler_" + username;
            this.consumerBeanName = "mqttConsumer_" + username;
            this.factoryBeanName = "mqttFactory_" + username;
            this.gatewayBeanName = "mqttGateway_" + username;
        }
    }

    /**
     * 一键创建/刷新连接
     *
     * @param username  用户名
     * @param password  密码
     * @return 可直接发消息的 MqttGateway
     */
    public synchronized MqttGateway connectOrRefresh(String username, String password) {
        Assert.notNull(username, "username cannot be null");
        Assert.notNull(password, "password cannot be null");

        String clientId = "mqttx-" + username;
        try {
            /* 1. 获取或创建连接信息 */
            ConnectionInfo connectionInfo = connectionMap.computeIfAbsent(username, ConnectionInfo::new);

            /* 2. 创建或更新专属工厂 */
            DefaultMqttPahoClientFactory factory;
            if (context.containsBean(connectionInfo.factoryBeanName)) {
                factory = context.getBean(connectionInfo.factoryBeanName, DefaultMqttPahoClientFactory.class);
                factory.setConnectionOptions(buildOptions(username, password, MQTT_URL));
                log.info("已更新 MQTT 客户端工厂 -> {}", connectionInfo.factoryBeanName);
            } else {
                factory = new DefaultMqttPahoClientFactory();
                factory.setConnectionOptions(buildOptions(username, password, MQTT_URL));
                context.registerBean(connectionInfo.factoryBeanName, DefaultMqttPahoClientFactory.class, () -> factory);
                log.info("已创建 MQTT 客户端工厂 -> {}", connectionInfo.factoryBeanName);
            }

            /* 3. 移除旧的 Handler 和 Consumer */
            removeOldConnection(connectionInfo);

            /* 4. 创建新的 Handler */
            MqttPahoMessageHandler handler = new MqttPahoMessageHandler(clientId, factory);
            handler.setAsync(true);
            handler.setDefaultTopic(defaultTopic);
            handler.afterPropertiesSet();

            /* 5. 注册新的 Handler */
            context.registerBean(connectionInfo.handlerBeanName, MqttPahoMessageHandler.class, () -> handler);

            /* 6. 创建并注册新的专属网关 */
            // 创建一个简单的Gateway实现，直接使用Handler发送消息
            MqttGateway gateway = new MqttGateway() {
                @Override
                public void sendToMqtt(String payload) {
                    try {
                        handler.handleMessage(org.springframework.messaging.support.MessageBuilder
                                .withPayload(payload)
                                .setHeader(MqttHeaders.TOPIC, defaultTopic)
                                .build());
                    } catch (Exception e) {
                        log.error("发送MQTT消息失败: {}", e.getMessage(), e);
                        throw new RuntimeException("发送MQTT消息失败", e);
                    }
                }

                @Override
                public void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload) {
                    try {
                        handler.handleMessage(org.springframework.messaging.support.MessageBuilder
                                .withPayload(payload)
                                .setHeader(MqttHeaders.TOPIC, topic)
                                .build());
                    } catch (Exception e) {
                        log.error("发送MQTT消息失败: {}", e.getMessage(), e);
                        throw new RuntimeException("发送MQTT消息失败", e);
                    }
                }

                @Override
                public void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic,
                                       @Header(MqttHeaders.QOS) int qos, String payload) {
                    try {
                        handler.handleMessage(org.springframework.messaging.support.MessageBuilder
                                .withPayload(payload)
                                .setHeader(MqttHeaders.TOPIC, topic)
                                .setHeader(MqttHeaders.QOS, qos)
                                .build());
                    } catch (Exception e) {
                        log.error("发送MQTT消息失败: {}", e.getMessage(), e);
                        throw new RuntimeException("发送MQTT消息失败", e);
                    }
                }
            };

            log.info("MQTT 连接刷新完成 -> {} / {}", username, clientId);
            return gateway;
        } catch (Exception e) {
            log.error("MQTT 连接失败 -> {}", clientId, e);
            throw new RuntimeException("MQTT 连接失败", e);
        }
    }

    /* ---------- 私有辅助 ---------- */

    private org.eclipse.paho.client.mqttv3.MqttConnectOptions
    buildOptions(String u, String p, String url) {
        org.eclipse.paho.client.mqttv3.MqttConnectOptions opt =
                new org.eclipse.paho.client.mqttv3.MqttConnectOptions();
        opt.setServerURIs(new String[]{url});
        opt.setUserName(u);
        if (p != null) opt.setPassword(p.toCharArray());
        opt.setKeepAliveInterval(keepAlive);
        return opt;
    }

    /**
     * 移除旧的连接实例
     * @param connectionInfo 连接信息
     */
    private void removeOldConnection(ConnectionInfo connectionInfo) {
        // 移除旧的 Handler
        if (context.containsBeanDefinition(connectionInfo.handlerBeanName)) {
            try {
                MqttPahoMessageHandler oldHandler = context.getBean(connectionInfo.handlerBeanName, MqttPahoMessageHandler.class);
                oldHandler.stop();
            } catch (Exception e) {
                log.warn("停止旧的MQTT处理器时出错: {}", e.getMessage(), e);
            }
            context.removeBeanDefinition(connectionInfo.handlerBeanName);
            log.info("已移除旧的 MQTT 处理器 -> {}", connectionInfo.handlerBeanName);
        }

        // 从单例缓存中移除旧的 Handler
        if (context.getDefaultListableBeanFactory().containsSingleton(connectionInfo.handlerBeanName)) {
            context.getDefaultListableBeanFactory().destroySingleton(connectionInfo.handlerBeanName);
        }

        // 移除旧的 Factory
        if (context.containsBeanDefinition(connectionInfo.factoryBeanName)) {
            context.removeBeanDefinition(connectionInfo.factoryBeanName);
            log.info("已移除旧的 MQTT 工厂 -> {}", connectionInfo.factoryBeanName);
        }

        // 从单例缓存中移除旧的 Factory
        if (context.getDefaultListableBeanFactory().containsSingleton(connectionInfo.factoryBeanName)) {
            context.getDefaultListableBeanFactory().destroySingleton(connectionInfo.factoryBeanName);
        }
    }

    /* ---------- 对外可调用的 setter ---------- */

    public MqttConnectionTool defaultTopic(String topic) {
        this.defaultTopic = topic;
        return this;
    }

    public MqttConnectionTool keepAlive(int keepAlive) {
        this.keepAlive = keepAlive;
        return this;
    }

    /* ---------- 复用原来的 Gateway 接口 ---------- */
    @MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
    public interface MqttGateway {
        void sendToMqtt(String payload);

        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload);

        void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic,
                        @Header(MqttHeaders.QOS) int qos, String payload);
    }
}
