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.ip.tcp.TcpMaster;
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.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * @author lenovo
 * @date 2025/6/27
 * @description modbus4j 单独提取出来的 读设备状态并设置时间
 */
@Component
public class DeviceStatusReaderAndTimeSetter {
    // 常量配置（优化重试，减少耗时）
    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 = 1; // 仅1次重试（减少总耗时）
    public static final int RETRY_DELAY = 200; // 重试间隔200ms（缩短等待）
    // 线程池：Spring管理，非静态（避免资源泄漏）
    private final ExecutorService deviceExecutor;

    // 构造函数：初始化线程池（核心线程=设备数/10，避免过多）
    public DeviceStatusReaderAndTimeSetter() {
        this.deviceExecutor = new ThreadPoolExecutor(
                4, // 核心线程4个（72个设备，4线程，每线程处理18个，效率适中）
                8, // 最大线程8个（峰值扩容）
                60,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(30), // 队列30个，避免任务堆积
                new ThreadFactory() {
                    private int count = 0;
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r, "modbus-device-pool-" + (count++));
                        thread.setDaemon(true); // 守护线程，不阻塞JVM退出
                        return thread;
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy() // 队列满时调用线程执行（避免任务丢失）
        );
    }

    // 工厂（单例）
    private static final ModbusFactory modbusFactory = new ModbusFactory();
    @Resource
    private ITEquipmentAlarmDataService alarmDataService;
    private static final org.slf4j.Logger log = 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);

        // 显式声明为TcpMaster，避免后续反射类型转换问题
        TcpMaster master = (TcpMaster) modbusFactory.createTcpMaster(params, true);
        master.setTimeout(3000); // 3秒连接超时
        master.setRetries(1);    // 1次重试
        master.init();
        log.debug("Modbus连接创建成功：IP={}, 端口={}, Master={}", ip, port, master);
        return master;
    }
    /**
     * 修正：直接从TcpMaster获取socket字段（适配Modbus4j 3.0.3版本）
     */
    private static Socket getUnderlyingSocket(ModbusMaster master) {
        if (!(master instanceof TcpMaster)) {
            log.error("ModbusMaster不是TcpMaster类型，无法获取Socket");
            return null;
        }

        TcpMaster tcpMaster = (TcpMaster) master;
        try {
            // 直接获取TcpMaster的private字段 "socket"（你的版本中直接存在该字段）
            Field socketField = TcpMaster.class.getDeclaredField("socket");
            socketField.setAccessible(true); // 取消私有字段访问限制
            Socket socket = (Socket) socketField.get(tcpMaster);

            if (socket != null) {
                log.debug("成功获取Socket：IP={}, 端口={}",
                        socket.getInetAddress(), socket.getPort());
            } else {
                log.debug("TcpMaster的socket字段为null（未建立连接）");
            }
            return socket;

        } catch (NoSuchFieldException e) {
            log.error("=== 字段未找到！===\n" +
                    "原因：TcpMaster中不存在socket字段（版本不匹配）\n" +
                    "解决方案：检查你的TcpMaster源码，确认Socket字段名（当前版本应为'socket'）", e);
        } catch (IllegalAccessException e) {
            log.error("反射访问权限失败（可能被安全管理器拦截）", e);
        } catch (Exception e) {
            log.error("获取Socket异常", e);
        }
        return null;
    }
    /**
     * 销毁Modbus连接（先关Socket，再销毁Master，彻底释放）
     */
    private static void destroyModbusMaster(ModbusMaster master, int deviceId) {
        if (master == null) return;

        try {
            // 1. 直接获取并关闭TcpMaster的socket字段
            Socket socket = getUnderlyingSocket(master);
            if (socket != null && !socket.isClosed()) {
                socket.close();
                log.debug("设备{}: 底层Socket已强制关闭", deviceId);
            }
        } catch (Exception e) {
            log.error("设备{}: 关闭Socket异常", deviceId, e);
        } finally {
            // 2. 调用自带的destroy()方法，双重保障
            try {
                master.destroy();
                log.debug("设备{}: ModbusMaster已销毁", deviceId);
            } catch (Exception e) {
                log.error("设备{}: 销毁ModbusMaster异常", deviceId, e);
            }
        }
    }
    /**
     * 启动多设备监控（新增portTimeout参数，统一超时）
     */
    public void startMultiDeviceMonitoring(
            String ip, int port, List<Integer> deviceIds,
            Consumer<DeviceStatusReaderDto> resultHandler,
            Predicate<int[]> stopCondition,
            int portTimeout) { // 单个端口总超时（秒）
        if (deviceIds == null || deviceIds.isEmpty()) {
            log.warn("设备ID列表为空，不执行监控：IP={}, 端口={}", ip, port);
            return;
        }

        final CountDownLatch latch = new CountDownLatch(deviceIds.size());
        log.debug("启动端口{}监控：IP={}，设备数={}，超时={}s", ip, port, deviceIds.size(), portTimeout);

        for (int deviceId : deviceIds) {
            final int devId = deviceId;
            deviceExecutor.submit(() -> {
                AtomicReference<ModbusMaster> threadMaster = null;
                try {
                    // 单个设备任务超时控制（portTimeout/设备数，避免整体超时）
                    int devTimeout = Math.max(1, portTimeout * 1000 / deviceIds.size());
                    ExecutorService devExecutor = Executors.newSingleThreadExecutor();
                    Future<?> devFuture = devExecutor.submit(() -> {
                        try {
                            // 1. 创建独立连接（每个设备一个，避免复用泄漏）
                            threadMaster.set(createModbusMaster(ip, port));
                            // 2. 读取数据（带重试）
                            int[] result = readWithConditionalRetry(threadMaster.get(), ip, port, devId, stopCondition);
                            // 3. 回调结果
                            if (resultHandler != null) {
                                resultHandler.accept(new DeviceStatusReaderDto(ip, port, devId, result));
                            }
                        } catch (Exception e) {
                            throw new RuntimeException("设备" + devId + "执行异常", e);
                        }
                    });

                    // 单个设备任务超时等待
                    devFuture.get(devTimeout, TimeUnit.MILLISECONDS);
                    devExecutor.shutdown();

                } catch (TimeoutException e) {
                    String errMsg = "设备" + devId + "超时：IP=" + ip + ", 端口=" + port;
                    log.error(errMsg, e);
                    recordAlarm("03", "ip:" + ip + ",port:" + port + ",deviceId:" + devId, errMsg);
                } catch (Exception e) {
                    String errMsg = "设备" + devId + "执行异常：IP=" + ip + ", 端口=" + port;
                    log.error(errMsg, e);
                    recordAlarm("03", "ip:" + ip + ",port:" + port + ",deviceId:" + devId, errMsg + "：" + e.getMessage());
                } finally {
                    // 强制释放资源（无论成功/失败）
                    destroyModbusMaster(threadMaster.get(), devId);
                    latch.countDown();
                    log.debug("设备{}完成：IP={}, 端口={}，剩余任务={}", devId, ip, port, latch.getCount());
                }
            });
        }

        // 端口总超时等待（确保不阻塞上层Job）
        try {
            if (!latch.await(portTimeout, TimeUnit.SECONDS)) {
                String errMsg = "端口" + port + "部分设备超时：IP=" + ip + "，未完成=" + latch.getCount();
                log.warn(errMsg);
                recordAlarm("03", "ip:" + ip + ",port:" + port, errMsg);
            }
        } catch (InterruptedException e) {
            log.error("端口{}监控被中断：IP={}", port, 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);
        }
    }
}
