package com.zehong.system.task;

import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.modbus.util.Modbus4jUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author lenovo
 * @date 2025/12/6
 * @description 实时读取老化层数据功能
 */
@Component
public class RealTimeReadAgingDataFunction {

    private static final int SINGLE_DEVICE_TIMEOUT_SEC = 10; // 单个设备超时：10秒
    private static final int TOTAL_TASK_TIMEOUT_SEC = 240; // 任务总超时：4分钟
    private static final Logger log = LoggerFactory.getLogger(RealTimeReadAgingDataFunction.class);

    @Resource
    private TStoreyInfoMapper tStoreyInfoMapper;

    // 全局线程池 - 避免重复创建
    private static final ExecutorService GLOBAL_DEVICE_EXECUTOR = new ThreadPoolExecutor(
            50, 100, 60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(500),
            r -> new Thread(r, "real-time-read-aging-data-device"),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    /**
     * 执行实时读取任务
     * @param storeyCode 楼层编码
     * @return 所有设备的读取结果列表
     */
    public List<DeviceResult> execute(String storeyCode) {
        long startTime = System.currentTimeMillis();
        List<DeviceResult> allResults = new ArrayList<>();
        List<CompletableFuture<List<DeviceResult>>> portFutures = null;
        try {
            TStoreyInfo storeyInfo = tStoreyInfoMapper.selectTStoreyInfoByCode(storeyCode);
            if (storeyInfo == null) {
                log.error("未找到对应的设备信息: storeyCode={}", storeyCode);
                return allResults;
            }

            // 并行处理3个端口，并收集结果
            portFutures = Arrays.asList(
                    processPort(storeyInfo, 501, Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27)),
                    processPort(storeyInfo, 502, Arrays.asList(28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54)),
                    processPort(storeyInfo, 503, Arrays.asList(55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72))
            );

            // 等待所有端口完成，带总超时
            CompletableFuture<Void> allPorts = CompletableFuture.allOf(
                    portFutures.toArray(new CompletableFuture[0])
            );

            allPorts.get(TOTAL_TASK_TIMEOUT_SEC, TimeUnit.SECONDS);

            // 收集所有端口的结果
            for (CompletableFuture<List<DeviceResult>> portFuture : portFutures) {
                List<DeviceResult> portResults = portFuture.get();
                if (portResults != null) {
                    allResults.addAll(portResults);
                }
            }

            log.info("实时读取任务执行成功: storeyCode={}, 总耗时={}ms, 读取设备数={}",
                    storeyCode, System.currentTimeMillis() - startTime, allResults.size());

        } catch (TimeoutException e) {
            log.warn("任务执行超时: storeyCode={}", storeyCode);
            // 超时时，尝试获取已经完成的结果
            allResults = getCompletedResults(portFutures);
        } catch (Exception e) {
            log.error("任务执行异常: storeyCode={}", storeyCode, e);
            // 异常时，尝试获取已经完成的结果
            allResults = getCompletedResults(portFutures);
        }

        return allResults;
    }

    /**
     * 获取已完成的任务结果
     */
    private List<DeviceResult> getCompletedResults(List<CompletableFuture<List<DeviceResult>>> portFutures) {
        List<DeviceResult> completedResults = new ArrayList<>();
        if (portFutures != null) {
            for (CompletableFuture<List<DeviceResult>> future : portFutures) {
                if (future.isDone() && !future.isCompletedExceptionally()) {
                    try {
                        List<DeviceResult> results = future.get();
                        if (results != null) {
                            completedResults.addAll(results);
                        }
                    } catch (Exception e) {
                        log.warn("获取已完成结果时发生异常", e);
                    }
                }
            }
        }
        return completedResults;
    }

    /**
     * 处理单个端口的所有设备，返回设备结果列表
     */
    private CompletableFuture<List<DeviceResult>> processPort(TStoreyInfo storeyInfo, int port, List<Integer> deviceIds) {
        return CompletableFuture.supplyAsync(() -> {
            String ip = storeyInfo.getfIp();
            String storeyIdStr = storeyInfo.getfStoreyId().toString();

            log.info("开始端口通信: ip={}, port={}, 设备数={}", ip, port, deviceIds.size());

            // 收集所有设备的结果
            List<CompletableFuture<DeviceResult>> deviceFutures = deviceIds.stream()
                    .map(deviceId -> processDevice(ip, port, deviceId))
                    .collect(Collectors.toList());

            List<DeviceResult> deviceResults;

            try {
                // 等待该端口所有设备完成
                CompletableFuture<Void> allDevices = CompletableFuture.allOf(
                        deviceFutures.toArray(new CompletableFuture[0])
                );

                // 端口超时 = 设备数 * 单设备超时 / 并发因子
                int portTimeout = Math.max(30, deviceIds.size() * SINGLE_DEVICE_TIMEOUT_SEC / 5);
                allDevices.get(portTimeout, TimeUnit.SECONDS);

                // 获取所有设备的结果
                deviceResults = deviceFutures.stream()
                        .map(future -> {
                            try {
                                return future.get();
                            } catch (Exception e) {
                                log.warn("获取设备结果时发生异常", e);
                                return null;
                            }
                        })
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());

                // 记录端口统计信息
                logPortStatistics(deviceResults, port, ip);

            } catch (TimeoutException e) {
                log.warn("端口{}通信超时: ip={}, fStoreyId={}", port, ip, storeyIdStr);
                // 超时时获取已完成的结果
                deviceResults = getCompletedDeviceResults(deviceFutures);
            } catch (Exception e) {
                log.error("端口{}通信异常: ip={}, fStoreyId={}", port, ip, storeyIdStr, e);
                // 异常时获取已完成的结果
                deviceResults = getCompletedDeviceResults(deviceFutures);
            }

            log.info("端口通信完成: ip={}, port={}, fStoreyId={}, 成功数={}/{}",
                    ip, port, storeyIdStr,
                    deviceResults.stream().filter(DeviceResult::isSuccess).count(),
                    deviceIds.size());

            return deviceResults;
        }, GLOBAL_DEVICE_EXECUTOR);
    }

    /**
     * 获取已完成的设备结果
     */
    private List<DeviceResult> getCompletedDeviceResults(List<CompletableFuture<DeviceResult>> deviceFutures) {
        return deviceFutures.stream()
                .filter(future -> future.isDone() && !future.isCompletedExceptionally())
                .map(future -> {
                    try {
                        return future.get();
                    } catch (Exception e) {
                        log.warn("获取已完成设备结果时发生异常", e);
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 记录端口统计信息
     */
    private void logPortStatistics(List<DeviceResult> results, int port, String ip) {
        if (results == null || results.isEmpty()) {
            log.warn("端口{}无任何结果: ip={}", port, ip);
            return;
        }

        long successCount = results.stream().filter(DeviceResult::isSuccess).count();
        long failCount = results.size() - successCount;

        // 记录成功和失败设备
        List<String> successDevices = results.stream()
                .filter(DeviceResult::isSuccess)
                .map(result -> result.getConvertedDeviceId() + ":" + Arrays.toString(result.getModbusResult()))
                .collect(Collectors.toList());

        List<String> failDevices = results.stream()
                .filter(result -> !result.isSuccess())
                .map(result -> result.getConvertedDeviceId() + ":" + result.getMessage())
                .collect(Collectors.toList());

        log.info("端口{}统计: 成功={}, 失败={}, ip={}", port, successCount, failCount, ip);

        if (!successDevices.isEmpty()) {
            log.debug("端口{}成功设备: {}", port, String.join(", ", successDevices));
        }

        if (!failDevices.isEmpty()) {
            log.warn("端口{}失败设备: {}", port, String.join(", ", failDevices));
        }
    }

    /**
     * 处理单个设备（仅读取）
     */
    private CompletableFuture<DeviceResult>  processDevice(String ip, int port, int deviceId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                // 1. 异步读取设备数据
                CompletableFuture<int[]> readFuture = CompletableFuture.supplyAsync(
                        () -> Modbus4jUtils.readDeviceWithRetry(ip, port, deviceId),
                        GLOBAL_DEVICE_EXECUTOR
                );

                // 设置读取超时
                int[] result = readFuture.get(SINGLE_DEVICE_TIMEOUT_SEC, TimeUnit.SECONDS);

                // 2. 转换设备ID格式
                String convertedDeviceId = convertDeviceId(deviceId);

                log.debug("设备{}读取成功: ip={}, port={}, status={}, convertedId={}",
                        deviceId, ip, port, result[1], convertedDeviceId);

                return createFullDeviceResult(deviceId, result, true, "读取成功");

            } catch (TimeoutException e) {
                log.error("设备{}读取超时: ip={}, port={}", deviceId, ip, port, e);
                return createFullDeviceResult(deviceId, null, false, "读取超时");
            } catch (Exception e) {
                log.error("设备{}处理异常: ip={}, port={}", deviceId, ip, port, e);
                return createFullDeviceResult(deviceId, null, false, "处理异常: " + e.getMessage());
            }
        }, GLOBAL_DEVICE_EXECUTOR);
    }
    /**
     * 设备ID转换方法（按照前端一条龙排列规则）
     * 设备布局：8行×9列 = 72个设备
     * 排列规则：
     * 1. 将8行分成3个段：第1-3行、第4-6行、第7-8行
     * 2. 每个段内按照"一条龙"方式排列：
     *    - 奇数行（段内第1、3行）：从左到右递增
     *    - 偶数行（段内第2行）：从右到左递增
     * 3. 每个段包含27个设备，分别编号为：
     *    - 第1段：1-27
     *    - 第2段：28-54
     *    - 第3段：55-72
     *
     * 前端计算规则：
     * row = Math.floor(i / 9) + 1 (1-8行)
     * col = (i % 9) + 1 (1-9列)
     *
     * 本方法输入deviceId(1-72)，输出"row-col"格式
     */
    private String convertDeviceId(int deviceId) {
        if (deviceId < 1 || deviceId > 72) {
            log.warn("设备ID超出范围: {}", deviceId);
            return "unknown-" + deviceId;
        }

        // 数组索引从0开始
        int index = deviceId - 1;

        // 计算行号（1-8行）
        int row = index / 9 + 1;

        // 计算列号（1-9列）
        int col = index % 9 + 1;

        return row + "-" + col;
    }
    /**
     * 设备ID转换方法（按照前端一条龙排列规则）
     * 设备布局：8行×9列 = 72个设备
     * 排列规则：
     * 1. 将8行分成3个段：第1-3行、第4-6行、第7-8行
     * 2. 每个段内按照"一条龙"方式排列：
     *    - 奇数行（段内第1、3行）：从左到右递增
     *    - 偶数行（段内第2行）：从右到左递增
     * 3. 每个段包含27个设备，分别编号为：
     *    - 第1段：1-27
     *    - 第2段：28-54
     *    - 第3段：55-72
     *
     * 前端计算规则：
     * row = Math.floor(i / 9) + 1 (1-8行)
     * col = (i % 9) + 1 (1-9列)
     *
     * 本方法输入deviceId(1-72)，输出"row-col"格式
     */
    /**
     * 获取设备在"一条龙"排列中的编号（前端显示的number）
     * 根据前端JavaScript逻辑实现
     */
    private int getDeviceNumberInSerpentine(int deviceId) {
        if (deviceId < 1 || deviceId > 72) {
            return -1;
        }

        // 数组索引从0开始
        int index = deviceId - 1;

        // 计算行号（1-8行）
        int row = index / 9 + 1;

        // 计算列号（1-9列）
        int col = index % 9 + 1;

        // 计算属于第几段（0,1,2）
        // 第1-3行：段0，第4-6行：段1，第7-8行：段2
        int segment;
        if (row <= 3) {
            segment = 0;
        } else if (row <= 6) {
            segment = 1;
        } else {
            segment = 2;
        }

        // 计算段内行号（0,1,2）
        int segmentRow = (row - 1) % 3;

        // 计算当前段的起始数字
        int segmentStart = segment * 27 + 1;

        // 每段内按照一条龙排列
        int number;
        if (segmentRow % 2 == 0) {
            // 奇数行（段内第1、3行）：从左到右递增
            number = segmentStart + segmentRow * 9 + (col - 1);
        } else {
            // 偶数行（段内第2行）：从右到左递增
            number = segmentStart + (segmentRow + 1) * 9 - col;
        }

        return number;
    }
    /**
     * 如果需要返回前端JavaScript中完整的设备信息，可以修改DeviceResult类
     * 并创建一个新方法来生成完整的设备信息
     */
    public DeviceResult createFullDeviceResult(int deviceId, int[] modbusResult, boolean success, String message) {
        String positionId = convertDeviceId(deviceId);
        int serpentineNumber = getDeviceNumberInSerpentine(deviceId);

        // 计算行号和列号
        int index = deviceId - 1;
        int row = index / 9 + 1;
        int col = index % 9 + 1;

        // 创建结果对象
        DeviceResult result = new DeviceResult();
        result.setConvertedDeviceId(positionId); // 格式：行-列，如"1-1", "1-2"
        result.setSerpentineNumber(serpentineNumber); // 一条龙排列中的编号
        result.setRow(row);
        result.setCol(col);
        if(modbusResult != null){
            result.setModbusResult(modbusResult);
            // 设置卡号
            StringBuilder simCardNoBuilder = new StringBuilder();
            for (int i = 17; i <= 36; i++) {
                simCardNoBuilder.append(modbusResult[i]);
            }
            String simCardNo = simCardNoBuilder.toString();
            result.setSimCard(simCardNo);
        }
        result.setSuccess(success);
        result.setMessage(message);

        return result;
    }
    /**
     * 设备处理结果封装类
     */
    public static class DeviceResult {
        private String convertedDeviceId;  // 位置ID：行-列，如"1-1"
        private Integer serpentineNumber;  // 一条龙排列中的编号（前端显示的number）
        private int row;                   // 行号（1-8）
        private int col;                   // 列号（1-9）
        private int[] modbusResult;        // Modbus读取结果
        private boolean success;           // 处理是否成功
        private String message;            // 处理消息

        private String simCard;

        public DeviceResult() {
        }

        public DeviceResult(String convertedDeviceId, Integer serpentineNumber, int row, int col, int[] modbusResult,
                            boolean success, String message, String simCard) {
            this.convertedDeviceId = convertedDeviceId;
            this.serpentineNumber = serpentineNumber;
            this.row = row;
            this.col = col;
            this.modbusResult = modbusResult;
            this.success = success;
            this.message = message;
            this.simCard = simCard;
        }

        public String getConvertedDeviceId() {
            return convertedDeviceId;
        }

        public void setConvertedDeviceId(String convertedDeviceId) {
            this.convertedDeviceId = convertedDeviceId;
        }

        public int[] getModbusResult() {
            return modbusResult;
        }

        public void setModbusResult(int[] modbusResult) {
            this.modbusResult = modbusResult;
        }

        public boolean isSuccess() {
            return success;
        }

        public void setSuccess(boolean success) {
            this.success = success;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        public Integer getSerpentineNumber() {
            return serpentineNumber;
        }

        public void setSerpentineNumber(Integer serpentineNumber) {
            this.serpentineNumber = serpentineNumber;
        }

        public int getRow() {
            return row;
        }

        public void setRow(int row) {
            this.row = row;
        }

        public int getCol() {
            return col;
        }

        public void setCol(int col) {
            this.col = col;
        }

        public String getSimCard() {
            return simCard;
        }

        public void setSimCard(String simCard) {
            this.simCard = simCard;
        }

        @Override
        public String toString() {
            return "DeviceResult{" +
                    "convertedDeviceId='" + convertedDeviceId + '\'' +
                    ", serpentineNumber=" + serpentineNumber +
                    ", row=" + row +
                    ", col=" + col +
                    ", modbusResult=" + Arrays.toString(modbusResult) +
                    ", success=" + success +
                    ", message='" + message + '\'' +
                    '}';
        }
    }
}
