package com.zehong.system.modbus.business;

import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.msg.ModbusResponse;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.modbus.handler.dto.DeviceStatusReaderDto;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * @author lenovo
 * @date 2025/6/27
 * @description modbus4j 单独提取出来的 读设备状态并设置时间
 */
@Component
public class DeviceStatusReaderAndTimeSetter {

    // 常量改为public以便外部访问
    public static final int START_ADDRESS = 0;
    public static final int REGISTER_COUNT = 10;
    public static final int TARGET_VALUE = 1;
    public static final int MAX_RETRIES = 3;
    public static final int RETRY_DELAY = 500;
    public static final int TIMEOUT_MINUTES = 5;
    public static final int TIMEOUT_SECONDS = 30; // 总超时30秒
    // 工厂
    private static final ModbusFactory modbusFactory = new ModbusFactory();

    // 1. 关键优化：固定线程池大小（不随设备数变化，根据CPU核数设置，如10）

    // 线程池优化：添加线程工厂，便于问题追踪
    private static final ExecutorService FIXED_THREAD_POOL = new ThreadPoolExecutor(
            5,
            10,
            60,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadFactory() {
                private int count = 0;
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r, "modbus-pool-" + (count++));
                    thread.setDaemon(true); // 设为守护线程，避免阻塞JVM退出
                    return thread;
                }
            },
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    @Resource
    private ITEquipmentAlarmDataService alarmDataService;

    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DeviceStatusReaderAndTimeSetter.class);
    /**
     * 读取设备寄存器（线程安全版）
     */
    private int[] readDeviceRegisters(ModbusMaster master, int deviceId )
            throws ModbusTransportException {

        // 创建读取请求
        ReadHoldingRegistersRequest request = Modbus4jUtils.getReadHoldingRegistersRequest(deviceId,START_ADDRESS, REGISTER_COUNT);

        // 发送请求并获取响应
        ModbusResponse response = master.send(request);

        // 检查响应类型
        if (!(response instanceof ReadHoldingRegistersResponse)) {
            throw new IllegalArgumentException("Invalid response type: " + response.getClass().getName());
        }

        ReadHoldingRegistersResponse regResponse = (ReadHoldingRegistersResponse) response;
        short[] signedValues = regResponse.getShortData();

        // 转换为无符号整数
        int[] unsignedValues = new int[signedValues.length];
        for (int i = 0; i < signedValues.length; i++) {
            unsignedValues[i] = signedValues[i] & 0xFFFF;
        }

        return unsignedValues;
    }
    /**
     * 带自定义条件的重试读取（线程安全版）
     */
    private int[] readWithConditionalRetry(ModbusMaster threadMaster,String ip, int port, int deviceId,
                                                  Predicate<int[]> conditionChecker)
            throws InterruptedException {

        TEquipmentAlarmData alarmData;

        int[] result = null;
        int attempt = 0;
        boolean conditionMet = false;

        if(threadMaster != null) {

            while (attempt <= MAX_RETRIES && !conditionMet) {
                attempt++;
                try {
                    log.info("当前 - 尝试第:{}次 读取设备ip:{} port:{} device:{}的数据", attempt, ip,port,deviceId);

                    // 读取数据
                    result = readDeviceRegisters(threadMaster, deviceId);

                    // 使用自定义条件检查
                    if (conditionChecker.test(result)) {
                        log.info("当前  设备ip:{} port:{} device:{}的数据条件满足", ip,port,deviceId);
                        conditionMet = true;
                    } else if (attempt <= MAX_RETRIES) {
                        log.info("当前  设备ip:{} port:{} device:{}的数据条件未满足，等待重试...", ip,port,deviceId);
                        Thread.sleep(RETRY_DELAY);
                    }
                } catch (Throwable e) {
                    log.error("设备{}: 第{}次读取异常", deviceId, attempt, e);
                    recordAlarm("04", "ip:" + ip + ",port:" + port + ",deviceId:" + deviceId, "第" + attempt + "次读取失败");
                    if (attempt <= MAX_RETRIES) {
                        Thread.sleep(RETRY_DELAY);
                    }
                }
            }

            // 如果达到最大重试次数仍未满足条件
            if (!conditionMet) {
                log.info("当前  设备ip:{} port:{} device:{}的尝试次数达到最大:{}", ip,port,deviceId,MAX_RETRIES);
            }
        }
        return result != null ? result : new int[REGISTER_COUNT];
    }

    /**
     * 创建新的Modbus连接
     */
    private static ModbusMaster createModbusMaster(String ip, int port) throws ModbusInitException {
        IpParameters params = new IpParameters();
        params.setHost(ip);
        params.setPort(port);

        ModbusMaster master = modbusFactory.createTcpMaster(params, true);
        master.setTimeout(3000);
        master.setRetries(1);
        master.init();

        return master;
    }
    /**
     * 启动多设备监控（核心方法）
     */
    public void startMultiDeviceMonitoring(
            String ip,
            int port,
            List<Integer> deviceIds,
            Consumer<DeviceStatusReaderDto> resultHandler,
            Predicate<int[]> stopCondition ) {
        if (deviceIds == null || deviceIds.isEmpty()) {
            log.warn("⚠️ 设备ID列表为空，不执行监控");
            return;
        }

        final CountDownLatch latch = new CountDownLatch(deviceIds.size());
        log.info("开始监控设备：IP={}, 端口={}, 设备数={}（线程池活跃线程：{}）",
                ip, port, deviceIds.size(), ((ThreadPoolExecutor) FIXED_THREAD_POOL).getActiveCount());

        for (int deviceId : deviceIds) {
            final int devId = deviceId;
            FIXED_THREAD_POOL.submit(() -> {
                ModbusMaster threadMaster = null;
                Socket underlyingSocket = null; // 新增：跟踪底层Socket
                try {
                    // 1. 创建Modbus连接并获取底层Socket（反射方式，根据Modbus4j版本调整）
                    threadMaster = createModbusMaster(ip, port);
                    // 注意：以下反射代码依赖Modbus4j内部实现，不同版本可能需要调整
                    // 目的是获取底层Socket，确保关闭
                    Object transport = threadMaster.getClass().getDeclaredField("transport").get(threadMaster);
                    underlyingSocket = (Socket) transport.getClass().getDeclaredField("socket").get(transport);

                    // 2. 读取数据（带超时控制）
                    int[] result = readWithConditionalRetry(threadMaster, ip, port, devId, stopCondition);

                    // 3. 处理结果（判空避免NPE）
                    if (resultHandler != null) {
                        resultHandler.accept(new DeviceStatusReaderDto(ip, port, devId, result));
                    }

                } catch (Throwable e) {
                    log.error("设备{}: 监控任务异常", devId, e);
                    String alarmMsg = e instanceof OutOfMemoryError ? "内存溢出：" + e.getMessage() : "监控任务异常：" + e.getMessage();
                    recordAlarm("03", "ip:" + ip + ",port:" + port + ",deviceId:" + devId, alarmMsg);
                } finally {
                    // 关键修复：优先关闭底层Socket，再销毁Master
                    if (underlyingSocket != null) {
                        try {
                            if (!underlyingSocket.isClosed()) {
                                underlyingSocket.close();
                                log.debug("设备{}: 底层Socket已关闭", devId);
                            }
                        } catch (Exception e) {
                            log.error("设备{}: Socket关闭失败", devId, e);
                        }
                    }
                    if (threadMaster != null) {
                        try {
                            threadMaster.destroy();
                            log.debug("设备{}: ModbusMaster已销毁", devId);
                        } catch (Exception e) {
                            log.error("设备{}: ModbusMaster销毁失败", devId, e);
                        }
                    }
                    latch.countDown();
                    log.info("设备{}: 监控任务完成（剩余：{}）", devId, latch.getCount());
                }
            });
        }

        // 等待任务完成，超时后中断未完成任务
        try {
            if (!latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
                log.warn("IP{}: 部分设备监控超时（未完成：{}），强制中断", ip, latch.getCount());
                recordAlarm("03", "ip:" + ip + ",port:" + port, "部分设备监控超时（未完成：" + latch.getCount() + "）");
                // 注意：线程池任务无法直接中断，需通过任务内部的中断机制（如Thread.interrupted()）
            }
        } catch (InterruptedException e) {
            log.error("IP{}: 监控任务被中断", ip, e);
            Thread.currentThread().interrupt();
        }
    }
    /**
     * 统一告警记录（抽离避免重复代码）
     */
    private void recordAlarm(String alarmType, String equipmentCode, String alarmData) {
        try {
            TEquipmentAlarmData alarm = new TEquipmentAlarmData();
            alarm.setfAlarmType(alarmType);
            alarm.setfEquipmentCode(equipmentCode);
            alarm.setfAlarmData(alarmData);
            alarm.setfCreateTime(new java.util.Date());
            alarmDataService.insertTEquipmentAlarmData(alarm);
        } catch (Exception e) {
            log.error("告警记录失败：{}", alarmData, e);
        }
    }
}
