package com.ruoyi.web.controller.system;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.system.domain.SysController;
import com.ruoyi.system.service.*;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;



/**
 * TDegine
 * 添加接驳庄数据到TDeinge
 */
@Component
public class TdegnineController5 {

    @Autowired
    private TDengineConnecting tdengineService;
    private final ObjectMapper objectMapper = new ObjectMapper();
    // MQTT 配置
    private static final String BROKER_URL = "tcp://47.104.204.180:1883";
    private static final String BROKER_HOST = "47.104.204.180";
    private static final int BROKER_PORT = 1883;
    private static final int QOS = 1;
    private static final int CONNECT_TIMEOUT = 3000;
    private static final int RECONNECT_INTERVAL = 5000;
    private static final int MAX_BATCH_SIZE = 50;
    private static final String MQTT_USERNAME = "NjniyrEO";
    private static final String MQTT_PASSWORD = "2b577892f4824d466dbc323a1ee4dfe1902c55bb";
    private MqttClient mqttClient;
    private MqttConnectOptions connOpts;
    private final Set<String> currentTopicSet = new CopyOnWriteArraySet<>();
    private final AtomicBoolean isConnected = new AtomicBoolean(false);
    private final Object lock = new Object();

    // 核心调度线程池
    private final ScheduledExecutorService coreExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread t = new Thread(r);
        t.setName("mqtt-core-scheduler");
        t.setDaemon(false); // ← 改这里！
        t.setPriority(Thread.NORM_PRIORITY - 1);
        return t;
    });

    // TDengine 写入线程池
    private final ExecutorService writeExecutor = new ThreadPoolExecutor(
            4, 8, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(5000),
            r -> {
                Thread t = new Thread(r);
                t.setName("td-write-worker-" + UUID.randomUUID().toString().substring(0, 4));
                t.setDaemon(false); // ← 改这里！
                return t;
            },
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    @PostConstruct
    @DependsOn({"sysControllerService", "tdengineService"})
    public void initMqttConnection() {
        System.out.println(">>> 开始初始化 MQTT 服务...");

        CompletableFuture<Boolean> connectFuture = CompletableFuture.supplyAsync(() -> {
            int initRetry = 0;
            while (initRetry < 3 && !isConnected.get()) {
                try {
                    if (refreshMqttSubscription()) {
                        System.out.println(">>> MQTT 初始化成功");
                        return true;
                    }
                } catch (Exception e) {
                    System.err.println("MQTT 启动初始化失败（第" + (initRetry + 1) + "次）：" + e.getMessage());
                    e.printStackTrace();
                }
                initRetry++;
                try {
                    Thread.sleep(RECONNECT_INTERVAL * initRetry);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
            return false;
        }, coreExecutor);

        try {
            Boolean connected = connectFuture.get(10, TimeUnit.SECONDS);
            if (!connected) {
                System.err.println("!!! MQTT 启动失败（超时），触发后台重连机制");
                triggerReconnect();
            }
        } catch (Exception e) {
            System.err.println("MQTT 初始化异常: " + e.getMessage());
            e.printStackTrace();
            triggerReconnect();
        }
    }

    private void initMqttConnectOptions() {
        if (connOpts != null) return;
        try {
            connOpts = new MqttConnectOptions();
            connOpts.setCleanSession(false);
            connOpts.setAutomaticReconnect(true);
            connOpts.setConnectionTimeout(10);
            connOpts.setKeepAliveInterval(60);
            connOpts.setMaxInflight(10);
            connOpts.setMaxReconnectDelay(30);
            connOpts.setUserName(MQTT_USERNAME);
            connOpts.setPassword(MQTT_PASSWORD.toCharArray());
        } catch (Exception e) {
            System.err.println("初始化 MQTT 配置失败：" + e.getMessage());
            throw new RuntimeException(e);
        }
    }

    public boolean refreshMqttSubscription() {
        synchronized (lock) {
            try {
                initMqttConnectOptions();
                List<String> latestTopicList =new ArrayList<>();
                latestTopicList.add("station/ChargeStation/device/+/post/json");
                System.out.println("🔍 查询到 Topic 列表: " + latestTopicList);

                if (latestTopicList == null || latestTopicList.isEmpty()) {
                    System.err.println("未查询到 Topic，取消所有订阅");
                    unsubscribeAll();
                    currentTopicSet.clear();
                    return false;
                }

                Set<String> latestTopicSet = new HashSet<>();
                for (String topic : latestTopicList) {
                    if (topic != null && !topic.trim().isEmpty()) {
                        latestTopicSet.add(topic.trim());
                    }
                }

                if (latestTopicSet.equals(currentTopicSet)) {
                    System.out.println("Topic 列表无变化，无需刷新订阅关系");
                    if (!isConnected.get() && !latestTopicSet.isEmpty()) {
                        return connectAndSubscribe(latestTopicSet);
                    }
                    return true;
                }

                Set<String> topicsToAdd = new HashSet<>(latestTopicSet);
                topicsToAdd.removeAll(currentTopicSet);
                Set<String> topicsToRemove = new HashSet<>(currentTopicSet);
                topicsToRemove.removeAll(latestTopicSet);

                if (!isConnected.get()) {
                    return connectAndSubscribe(latestTopicSet);
                } else {
                    if (!topicsToRemove.isEmpty()) unsubscribeTopics(topicsToRemove);
                    if (!topicsToAdd.isEmpty()) subscribeTopics(topicsToAdd);
                }

                currentTopicSet.clear();
                currentTopicSet.addAll(latestTopicSet);
                System.out.println("MQTT 订阅刷新完成，当前数量：" + currentTopicSet.size());
                return true;

            } catch (Exception e) {
                System.err.println("MQTT 订阅刷新失败：" + e.getMessage());
                e.printStackTrace();
                return false;
            }
        }
    }

    private boolean connectAndSubscribe(Set<String> topicSet) {
        synchronized (lock) {
            if (isConnected.get() && mqttClient != null && mqttClient.isConnected()) {
                return true;
            }
            try {
                if (!checkServerAvailability()) {
                    System.err.println("Broker 不可达，连接失败");
                    return false;
                }

                if (mqttClient == null) {
                    String clientId = generateUniqueClientId();
                    MemoryPersistence persistence = new MemoryPersistence();
                    mqttClient = new MqttClient(BROKER_URL, clientId, persistence);
                    setMqttCallback();
                }

                int connectRetry = 0;
                while (connectRetry < 3 && !mqttClient.isConnected()) {
                    try {
                        mqttClient.connect(connOpts);
                        if (mqttClient.isConnected()) {
                            isConnected.set(true);
                            System.out.println("MQTT 连接成功，客户端 ID：" + mqttClient.getClientId());
                            break;
                        }
                    } catch (MqttException e) {
                        System.err.println("MQTT 连接失败（第" + (connectRetry + 1) + "次）：" + e.getMessage());
                        connectRetry++;
                        if (connectRetry < 3) Thread.sleep(RECONNECT_INTERVAL * (connectRetry + 1));
                    }
                }

                if (!mqttClient.isConnected()) {
                    isConnected.set(false);
                    return false;
                }

                if (!topicSet.isEmpty()) {
                    List<String> topicList = new ArrayList<>(topicSet);
                    for (int i = 0; i < topicList.size(); i += MAX_BATCH_SIZE) {
                        int end = Math.min(i + MAX_BATCH_SIZE, topicList.size());
                        List<String> batch = topicList.subList(i, end);
                        batchSubscribeTopics(new HashSet<>(batch));
                        Thread.sleep(100);
                    }
                }
                return true;

            } catch (Exception e) {
                System.err.println("MQTT 连接 + 订阅失败：" + e.getMessage());
                e.printStackTrace();
                isConnected.set(false);
                triggerReconnect();
                return false;
            }
        }
    }




    private void batchSubscribeTopics(Set<String> topicSet) {
        if (topicSet.isEmpty() || !isConnected.get() || mqttClient == null) return;
        try {
            List<String> topicList = new ArrayList<>(topicSet);
            String[] topics = topicList.toArray(new String[0]);
            int[] qosArr = new int[topics.length];
            Arrays.fill(qosArr, QOS);
            mqttClient.subscribe(topics, qosArr);
            System.out.println("订阅完成：" + topicList.size() + "个 Topic");
        } catch (MqttException e) {
            System.err.println("批量订阅失败：" + e.getMessage());
            retrySubscribeBatch(new ArrayList<>(topicSet), 1);
        }
    }

    private void retrySubscribeBatch(List<String> batchTopics, int retryTimes) {
        for (int retry = 1; retry <= retryTimes; retry++) {
            try {
                Thread.sleep(1000 * retry);
                if (!isConnected.get() || mqttClient == null) continue;
                String[] topics = batchTopics.toArray(new String[0]);
                int[] qosArr = new int[topics.length];
                Arrays.fill(qosArr, QOS);
                mqttClient.subscribe(topics, qosArr);
                System.out.println("重试订阅成功（第" + retry + "次），数量：" + batchTopics.size());
                return;
            } catch (Exception e) {
                System.err.println("重试订阅失败（第" + retry + "次）：" + e.getMessage());
            }
        }
        System.err.println("批次订阅最终失败：" + batchTopics);
    }

    private void subscribeTopics(Set<String> topicsToAdd) {
        if (topicsToAdd.isEmpty() || !isConnected.get()) return;
        batchSubscribeTopics(topicsToAdd);
    }

    private void unsubscribeTopics(Set<String> topicsToRemove) {
        if (topicsToRemove.isEmpty() || !isConnected.get() || mqttClient == null) return;
        try {
            String[] topics = topicsToRemove.toArray(new String[0]);
            mqttClient.unsubscribe(topics);
            System.out.println("取消订阅完成，数量：" + topicsToRemove.size());
        } catch (MqttException e) {
            System.err.println("取消订阅失败：" + e.getMessage());
        }
    }

    private void unsubscribeAll() {
        if (currentTopicSet.isEmpty() || !isConnected.get() || mqttClient == null) return;
        try {
            String[] topics = currentTopicSet.toArray(new String[0]);
            mqttClient.unsubscribe(topics);
            System.out.println("取消所有订阅，数量：" + currentTopicSet.size());
        } catch (MqttException e) {
            System.err.println("取消所有订阅失败：" + e.getMessage());
        }
    }

    private void triggerReconnect() {
        coreExecutor.schedule(() -> {
            int maxAttempts = 3;
            int attempt = 1;
            while (attempt <= maxAttempts && !isConnected.get()) {
                try {
                    System.out.println("MQTT 重连（第" + attempt + "次）");
                    if (connectAndSubscribe(currentTopicSet)) {
                        System.out.println("MQTT 重连成功");
                        break;
                    }
                    attempt++;
                    if (attempt <= maxAttempts) Thread.sleep(RECONNECT_INTERVAL * 2 * attempt);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    System.err.println("MQTT 重连失败（第" + attempt + "次）：" + e.getMessage());
                    attempt++;
                }
            }
            if (attempt > maxAttempts) {
                System.err.println("MQTT 重连达最大次数，停止尝试");
                isConnected.set(false);
            }
        }, 5, TimeUnit.SECONDS);
    }

    private boolean checkServerAvailability() {
        try (Socket socket = new Socket()) {
            socket.setSoTimeout(CONNECT_TIMEOUT);
            socket.setTcpNoDelay(true);
            socket.connect(new InetSocketAddress(BROKER_HOST, BROKER_PORT), CONNECT_TIMEOUT);
            return true;
        } catch (Exception e) {
            System.err.println("MQTT Broker 不可达：" + e.getMessage());
            return false;
        }
    }

    private String generateUniqueClientId() {
        String osPrefix = System.getProperty("os.name").toLowerCase().contains("windows") ? "mqtt_win_" : "mqtt_linux_";
        return osPrefix + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().replace("-", "").substring(0, 8);
    }

    private void setMqttCallback() {
        if (mqttClient == null) return;
        mqttClient.setCallback(new MqttCallback() {
            @Override
            public void connectionLost(Throwable cause) {
                System.err.println("MQTT 连接断开：" + cause.getMessage());
                isConnected.set(false);
                coreExecutor.schedule(() -> triggerReconnect(), 5, TimeUnit.SECONDS);
            }

            @Override
            public void messageArrived(String topic, MqttMessage message) {
                // 直接提交到写入线程池处理
                writeExecutor.submit(() -> {
                    try {
                        String content = new String(message.getPayload(), StandardCharsets.UTF_8);
                        if (content == null || content.trim().isEmpty()) return;
                        List<Map<String, Object>> messageList = objectMapper.readValue(content, new TypeReference<List<Map<String, Object>>>() {});

//                        Map<String, Object> messageMap = objectMapper.readValue(content, new TypeReference<Map<String, Object>>() {});
                        processMessageAndWriteToTDengine(messageList, topic);
                    } catch (Exception e) {
                        System.err.println("MQTT 消息处理失败（Topic：" + topic + "）：" + e.getMessage());
                        e.printStackTrace();
                    }
                });
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken token) {
                try { token.waitForCompletion(1000); } catch (MqttException ignored) {}
            }
        });
    }

    // 参数改为 List
    private void processMessageAndWriteToTDengine(List<Map<String, Object>> dataList, String topic) throws Exception {
        String[] topicParts = topic.split("/");
        if (topicParts.length < 2) {
            throw new IllegalArgumentException("无效的 Topic 格式：" + topic);
        }
        // TDengine 批量插入通常需要一个列表
        List<Map<String, Object>> batchToInsert = new ArrayList<>();
        for (Map<String, Object> dataMap : dataList) {
            // 检查数据是否为空
            if (dataMap == null || dataMap.isEmpty()) {
                continue;
            }
            // 深拷贝
            Map<String, Object> list = deepCopyMap(dataMap);
            batchToInsert.add(list);
        }

        // 如果有数据，才写入数据库
        if (!batchToInsert.isEmpty()) {
            String dbName = topicParts[1];
            String superTable = topicParts[3];
            LocalDate date = LocalDate.now();
            String tableName = superTable + "_" + date.getYear() + date.getMonthValue();
            // 调用写入方法（假设 insertBatch 接收 List）
            tdengineService.insertBatch(dbName, tableName, batchToInsert);
        }
    }

    private Map<String, Object> deepCopyMap(Map<String, Object> original) {
        if (original == null) return new HashMap<>();
        try {
            String json = objectMapper.writeValueAsString(original);
            return objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
        } catch (Exception e) {
            Map<String, Object> copy = new HashMap<>(original.size());
            original.forEach((k, v) -> {
                if (v instanceof Map) {
                    copy.put(k, deepCopyMap((Map<String, Object>) v));
                } else {
                    copy.put(k, v);
                }
            });
            return copy;
        }
    }

    public void disconnect() {
        synchronized (lock) {
            shutdownExecutor(writeExecutor, "TDengine 写入");

            if (mqttClient != null) {
                try {
                    if (mqttClient.isConnected()) {
                        unsubscribeAll();
                        mqttClient.disconnect(10000);
                    }
                    mqttClient.close();
                    System.out.println("MQTT 连接已断开");
                } catch (MqttException e) {
                    System.err.println("断开 MQTT 连接失败：" + e.getMessage());
                } finally {
                    isConnected.set(false);
                    mqttClient = null;
                }
            }
            if (tdengineService != null) tdengineService.close();
        }
    }

    private void shutdownExecutor(ExecutorService executor, String name) {
        if (executor == null || executor.isShutdown()) return;
        try {
            executor.shutdown();
            if (!executor.awaitTermination(20, TimeUnit.SECONDS)) {
                List<Runnable> remaining = executor.shutdownNow();
                System.err.println(name + " 线程池强制关闭，剩余任务数：" + remaining.size());
            } else {
                System.out.println(name + " 线程池已优雅关闭");
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
            System.err.println(name + " 线程池关闭被中断");
        }
    }

    @PreDestroy
    public void destroy() {
        System.out.println(">>> 服务正在关闭...");
        disconnect();
        shutdownExecutor(coreExecutor, "MQTT 核心");
    }


}