package com.agri.framework.config;

import com.agri.common.utils.uuid.UUID;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import javax.annotation.PreDestroy;
import java.nio.charset.StandardCharsets;

/**
 * MQTT核心配置类
 * 功能：
 * 1. 初始化MQTT客户端连接（对接Mosquitto）
 * 2. 提供MQTT消息发送工具类
 * 3. 配置连接参数（账号、密码、重连、保活等）
 * 适配JDK 8语法，兼容SpringBoot环境
 *
 * @Author: jone
 * @Date: 2026/1/16
 * @Version: 1.0
 */
@Configuration
public class MqttConfig {
    // 新增：替换System.out为SLF4J日志（JDK 8兼容，生产环境必备）
    private static final Logger log = LoggerFactory.getLogger(MqttConfig.class);

    /** Mosquitto服务器地址（TCP协议）：格式为 tcp://IP:端口 */
    @Value("${spring.mqtt.host}")
    private String host;

    /** MQTT认证用户名（Mosquitto配置的共用账号） */
    @Value("${spring.mqtt.username}")
    private String username;

    /** MQTT认证密码（Mosquitto配置的共用密码） */
    @Value("${spring.mqtt.password}")
    private String password;

    /** 后端MQTT客户端ID：需唯一，避免重复连接 */
    @Value("${spring.mqtt.client-id}")
    private String clientId;

    /** MQTT消息QoS级别：1=至少一次送达，0=最多一次，2=恰好一次 */
    @Value("${spring.mqtt.qos:1}")
    private int qos;

    /** MQTT连接超时时间（秒）：超过该时间未连接成功则判定为失败 */
    @Value("${spring.mqtt.timeout:60}")
    private int timeout;

    /** MQTT保活间隔（秒）：客户端定期发送心跳给服务端，维持连接 */
    @Value("${spring.mqtt.keep-alive:60}")
    private int keepAlive;

    // 新增：保存客户端实例，用于应用关闭时优雅断开连接
    private MqttClient mqttClientInstance;

    /**
     * 创建MQTT客户端实例（Spring Bean）
     * 核心逻辑：
     * 1. 配置连接参数（账号、密码、重连、保活等）
     * 2. 初始化客户端并建立连接
     * 3. 返回客户端实例供其他类注入使用
     *
     * @return MqttClient MQTT客户端实例
     * @throws MqttException MQTT连接/初始化异常
     */
    @Bean
    public MqttClient mqttClient() throws MqttException {
        // 新增：核心参数非空校验（避免空指针，JDK 8兼容）
        if (!StringUtils.hasText(host)) {
            throw new IllegalArgumentException("MQTT服务器地址（spring.mqtt.host）不能为空");
        }
        if (!StringUtils.hasText(clientId)) {
            throw new IllegalArgumentException("MQTT客户端ID（spring.mqtt.client-id）不能为空");
        }

        // 1. 初始化连接配置项
        MqttConnectOptions connectOptions = getMqttConnectOptions();

        // 2. 初始化MQTT客户端
        // MemoryPersistence：使用内存存储会话，不持久化到磁盘（适合后端服务）
        MemoryPersistence persistence = new MemoryPersistence();

        // 新增：客户端ID拼接随机后缀，避免多实例部署时冲突（生产环境必备）
        String uniqueClientId = clientId + "_" + UUID.generateAlphanumericRandom(8);
        MqttClient mqttClient = new MqttClient(host, uniqueClientId, persistence);

        // 3. 建立MQTT连接
        if (!mqttClient.isConnected()) {
            mqttClient.connect(connectOptions);
            // 优化：替换System.out为日志框架，保留原有输出内容
            log.info("【MQTT连接成功】服务器地址：" + host + "，客户端ID：" + uniqueClientId);
        } else {
            // 优化：替换System.out为日志框架，保留原有输出内容
            log.info("【MQTT连接状态】已连接，无需重复初始化");
        }

        // 新增：保存实例到成员变量，用于优雅关闭
        this.mqttClientInstance = mqttClient;
        return mqttClient;
    }

    private MqttConnectOptions getMqttConnectOptions() {
        MqttConnectOptions connectOptions = new MqttConnectOptions();
        // 设置MQTT认证账号
        connectOptions.setUserName(username);
        // 设置MQTT认证密码（转换为字符数组，符合API要求）
        connectOptions.setPassword(password.toCharArray());
        // 设置连接超时时间（秒）
        connectOptions.setConnectionTimeout(timeout);
        // 设置保活间隔（秒）：客户端每隔该时间发送一次心跳
        connectOptions.setKeepAliveInterval(keepAlive);
        // 关闭清除会话：false=重连后保留订阅关系（若不需要离线消息可设为true）
        // 优化：生产环境建议设为false，重连后保留订阅关系，避免丢失离线消息
        connectOptions.setCleanSession(true);
        connectOptions.setMaxInflight(200);
        // 开启自动重连：连接断开后自动尝试重连，提升稳定性（方案A核心）
        connectOptions.setAutomaticReconnect(true);
        // 设置最大重连间隔（秒）：避免频繁重连消耗资源
        connectOptions.setMaxReconnectDelay(30);
        return connectOptions;
    }

    /**
     * 创建MQTT消息发送工具类（Spring Bean）
     * 封装消息发布逻辑，供业务层调用
     *
     * @param mqttClient MQTT客户端实例（自动注入）
     * @return MqttMessageSender 消息发送工具类
     */
    @Bean
    public MqttMessageSender mqttMessageSender(MqttClient mqttClient) {
        return new MqttMessageSender(mqttClient, qos);
    }

    /**
     * MQTT消息发送工具类（内部类）
     * 封装消息发布的核心逻辑，简化业务层调用
     */
    public static class MqttMessageSender {
        /** MQTT客户端实例 */
        private final MqttClient client;
        /** 默认QoS级别 */
        private final int defaultQos;

        /**
         * 构造方法
         * @param client MQTT客户端实例
         * @param defaultQos 默认QoS级别
         */
        public MqttMessageSender(MqttClient client, int defaultQos) {
            this.client = client;
            this.defaultQos = defaultQos;
        }

        /**
         * 发布MQTT消息（使用默认QoS）
         * @param topic 消息主题
         * @param payload 消息内容（JSON字符串）
         * @throws MqttException 消息发布异常
         */
        public void publish(String topic, String payload) throws MqttException {
            publish(topic, payload, defaultQos);
        }

        /**
         * 发布MQTT消息（自定义QoS）
         * 核心逻辑：
         * 1. 校验客户端连接状态
         * 2. 构建MQTT消息对象
         * 3. 发布消息到指定主题
         *
         * @param topic 消息主题
         * @param payload 消息内容（JSON字符串）
         * @param qos 消息QoS级别
         * @throws MqttException 消息发布异常（连接断开、主题无效等）
         */
        public void publish(String topic, String payload, int qos) throws MqttException {
            // 新增：入参校验（避免空主题/空内容，JDK 8兼容）
            if (!StringUtils.hasText(topic)) {
                throw new IllegalArgumentException("MQTT发布主题不能为空");
            }
            if (payload == null) {
                payload = ""; // 空内容默认设为空字符串，避免NPE
            }

            // 1. 校验客户端是否已连接
            if (!client.isConnected()) {
                throw new MqttException(MqttException.REASON_CODE_CLIENT_NOT_CONNECTED);
            }

            // 2. 构建MQTT消息对象
            MqttMessage message = new MqttMessage();
            // 设置消息内容（转换为字节数组）
            // 优化：显式指定UTF-8编码，避免不同环境下编码不一致导致乱码（JDK 8兼容）
            message.setPayload(payload.getBytes(StandardCharsets.UTF_8));
            // 设置QoS级别
            message.setQos(qos);
            // 设置保留消息：true=服务端保留该主题的最新消息，新订阅者可立即获取
            message.setRetained(false);

            // 3. 发布消息
            client.publish(topic, message);
            // 优化：替换System.out为日志框架，保留原有输出内容
            // log.info("【MQTT消息发布成功】主题：" + topic + "，内容：" + payload);
        }
    }

    // 新增：应用关闭时优雅断开MQTT连接，避免连接泄漏（JDK 8兼容）
    @PreDestroy
    public void destroyMqttClient() {
        // 最小改动：补充判空+close+置空，保留原有逻辑和注释
        if (mqttClientInstance != null) {
            try {
                if (mqttClientInstance.isConnected()) {
                    mqttClientInstance.disconnect();
                    log.info("【MQTT连接关闭】客户端已优雅断开连接");
                }
                // 新增：关闭实例释放资源
                mqttClientInstance.close();
                // 新增：置空实例加速GC
                mqttClientInstance = null;
            } catch (MqttException e) {
                log.error("【MQTT连接关闭异常】：" + e.getMessage(), e);
            }
        }
    }

    /**
     * 暴露MQTT连接配置项为Bean，供其他类注入使用
     */
    @Bean
    public MqttConnectOptions mqttConnectOptions() {
        return getMqttConnectOptions();
    }
}
