package com.zehong.system.task.DeviceCommJob;

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.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import com.zehong.system.domain.PalletDeviceBinding;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.PalletDeviceBindingMapper;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.modbus.handler.ModbusResultHandler;
import com.zehong.system.modbus.handler.dto.DeviceStatusReaderDto;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.modbus.util.ModbusMasterPool;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.slf4j.Logger;
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.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
/**
 * @author lenovo
 * @date 2025/9/25
 * @description TODO
 */
@Component
public abstract  class BaseDeviceCommJob implements Job {
    private static final Logger log = LoggerFactory.getLogger(BaseDeviceCommJob.class);

    // -------------------------- 核心常量（仅保留必要配置，删除冗余） --------------------------
    // 超时配置：适配单设备场景，无需预留多设备缓冲
    private static final int SINGLE_DEVICE_TIMEOUT_SEC = 10; // 单个设备总超时
    private static final int MODBUS_CONN_TIMEOUT_MS = 3000; // Modbus连接超时
    // 重试配置：单设备重试1次足够，避免过度占用连接
    private static final int CUSTOM_RETRY_TIMES = 3;
    private static final int RETRY_DELAY_MS = 200;
    // Modbus寄存器固定配置（单设备无需动态调整）
    private static final int REG_START_ADDR = 0;
    private static final int REG_READ_COUNT = 10;
    // 单例Modbus工厂：避免重复创建开销
    private static final ModbusFactory modbusFactory = new ModbusFactory();

    // -------------------------- 公共依赖注入 --------------------------
    @Resource
    protected ITEquipmentAlarmDataService alarmDataService;
    @Resource
    protected TStoreyInfoMapper tStoreyInfoMapper;
    @Resource
    protected ModbusResultHandler resultHandler;


    @Resource
    private Scheduler scheduler;
    @Resource
    private PalletDeviceBindingMapper palletDeviceBindingMapper;

    // -------------------------- 抽象方法：子类实现差异化 --------------------------
    /**
     * 获取当前Job的固定端口号（501/502/503）
     */
    protected abstract int getFixedPort();

    /**
     * 获取当前端口对应的设备ID列表
     */
    protected abstract int getDeviceId();

    // -------------------------- 核心执行逻辑（单设备直连，无冗余步骤） --------------------------
    @Override
    public void execute(JobExecutionContext context) {

        // 新增：校验依赖是否为null
        log.info("=== 依赖注入校验 ===");
        log.info("scheduler: {}", scheduler == null ? "NULL" : "OK");
        log.info("palletDeviceBindingMapper: {}", palletDeviceBindingMapper == null ? "NULL" : "OK");
        log.info("alarmDataService: {}", alarmDataService == null ? "NULL" : "OK");
        log.info("tStoreyInfoMapper: {}", tStoreyInfoMapper == null ? "NULL" : "OK");
        log.info("resultHandler: {}", resultHandler == null ? "NULL" : "OK");

        Long fStoreyId = null;
        Integer port = null;
        Integer deviceId = null;
        TStoreyInfo storeyInfo = null;
        long taskStartTime = System.currentTimeMillis();

        try {
            // 1. 正确获取参数
            JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

            // 关键修复：直接获取，不需要转换
            fStoreyId = jobDataMap.getLong("fStoreyId");
            port = jobDataMap.getInt("port");
            deviceId = jobDataMap.getInt("deviceId");

            log.info("=== 开始执行设备任务：storeyId={}, port={}, deviceId={} ===",
                    fStoreyId, port, deviceId);

            log.info("=== 设备任务执行完成：storeyId={}, port={}, deviceId={} ===",
                    fStoreyId, port, deviceId);

            storeyInfo = tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);

            if(storeyInfo == null) {
                log.info("设备不存在：fStoreyId={}", fStoreyId);
                return;
            }
            String deviceIp = storeyInfo.getfIp();

            // 2. 执行单设备Modbus通信（带超时+重试）
            int[] modbusResult = executeModbusWithTimeout(deviceIp, port, deviceId);

            // 3. 处理Modbus结果（非必须，为空则跳过）
            if (resultHandler != null && modbusResult != null) {
                resultHandler.accept(new DeviceStatusReaderDto(deviceIp, port, deviceId, modbusResult));
            } else {
                updateDeviceError(storeyInfo.getfIp());
            }

            log.info("单设备任务成功：port={}, deviceId={}, storeyId={}，耗时={}ms",
                    port, deviceId, fStoreyId, System.currentTimeMillis() - taskStartTime);

        } catch (Throwable e) {
            // 单设备异常直接捕获，记录告警并抛出让Quartz感知
            String errMsg = String.format("单设备任务失败：port=%d, deviceId=%d, storeyId=%s",
                    getFixedPort(), deviceId, fStoreyId);
            log.info(errMsg, e);
            if(fStoreyId != null) {
                updateDeviceError(storeyInfo.getfIp());
            }
//            recordAlarm(storeyInfo, storeyIdStr, errMsg + "：" + e.getMessage());
        } finally {
            // 核心：任务执行完成后，清理Job和Trigger（无论成功/失败）
//            cleanJobAndTrigger(jobKey, triggerKey);
            log.info("单设备任务结束：port={}, deviceId={}, storeyId={}，耗时={}ms",
                    port, deviceId, fStoreyId, System.currentTimeMillis() - taskStartTime);
        }
    }
    /**
     * 清理Job和Trigger，避免元数据残留
     */
    private void cleanJobAndTrigger(JobKey jobKey, TriggerKey triggerKey) {
        if (jobKey == null || triggerKey == null) {
            log.warn("清理任务失败：JobKey或TriggerKey为空");
            return;
        }

        try {
            // 1. 先解绑Trigger（避免删除Job时失败）
            if (scheduler.checkExists(triggerKey)) {
                boolean unscheduleSuccess = scheduler.unscheduleJob(triggerKey);
                log.debug("{}：Trigger解绑{}，key={}",
                        unscheduleSuccess ? "成功" : "失败",
                        unscheduleSuccess ? "成功" : "失败",
                        triggerKey);
            }

            // 2. 再删除Job（删除Job会自动删除关联的Trigger，但保险起见先解绑）
            if (scheduler.checkExists(jobKey)) {
                boolean deleteSuccess = scheduler.deleteJob(jobKey);
                log.debug("{}：Job删除{}，key={}",
                        deleteSuccess ? "成功" : "失败",
                        deleteSuccess ? "成功" : "失败",
                        jobKey);
            }
        } catch (SchedulerException e) {
            log.error("清理任务元数据失败：jobKey={}, triggerKey={}", jobKey, triggerKey, e);
        }
    }
    // -------------------------- 精简后的核心工具方法 --------------------------
    /**
     * 提取单个deviceId（子类返回的List仅1个元素，避免集合遍历）
     */
    private Integer getSingleDeviceId() {
        return getDeviceId();
    }

    /**
     * 校验基础参数（storeyId、设备信息、IP），失败直接抛异常
     */
    private TStoreyInfo validateBaseParams(String storeyIdStr) {
        // 1. 校验storeyId格式
        if (StringUtils.isBlank(storeyIdStr)) { 
            log.info("storeyId为空：port=" + getFixedPort());
        }
        Long storeyId = parseStoreyId(storeyIdStr);
        if(storeyId == null) {
            log.info("storeyId格式错误：port=" + getFixedPort());
            return null;
        }

        // 2. 校验设备信息和IP
        TStoreyInfo storeyInfo = tStoreyInfoMapper.selectTStoreyInfoById(storeyId);
        if (storeyInfo == null || StringUtils.isBlank(storeyInfo.getfIp())) {
            log.info("设备信息无效：storeyId="+storeyId); 
        }
        return storeyInfo;
    }

    /**
     * 解析storeyId（单独抽离，减少代码冗余）
     */
    private Long parseStoreyId(String storeyIdStr) {
        try {
            return Long.parseLong(storeyIdStr);
        } catch (NumberFormatException e) {
            log.info("设备信息无效：storeyId="+storeyIdStr); 
        }
        return null;
    }

    /**
     * 单设备Modbus通信（带超时+重试，无线程池，用Future做超时控制）
     */
    private int[] executeModbusWithTimeout(String ip, int port, int deviceId) throws Exception {
        // 用单线程池做超时控制（比自定义线程更轻量，执行完自动销毁）
        ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
        try {
            Future<int[]> future = singleExecutor.submit(() ->
                    readModbusWithRetry(ip, port, deviceId)
            );
            // 超时控制：超过SINGLE_DEVICE_TIMEOUT_SEC秒直接中断
            return future.get(SINGLE_DEVICE_TIMEOUT_SEC, TimeUnit.SECONDS);
        } finally {
            // 强制关闭线程池，避免资源泄漏（单线程池关闭开销极小）
            singleExecutor.shutdownNow();
        }
    }

    /**
     * Modbus读取（带重试，连接用完即时销毁）
     */
    private int[] readModbusWithRetry(String ip, int port, int deviceId) throws Exception {
        // 1. 从连接池借连接（超时3秒
        ModbusMaster master = Modbus4jUtils.getMaster(ip, port);
        for (int retry = 0; retry <= CUSTOM_RETRY_TIMES; retry++) {
            try {
                // 2. 读取寄存器
                int[] result = readRegisters(master, deviceId);
                // 3. 校验结果（满足停止条件则返回，否则重试）
                if (isValidResult(result)) {
                    log.info("Modbus读取成功：retry={}, port={}, deviceId={}", retry, port, deviceId);
                    return result;
                }
                log.info("Modbus结果不满足条件，重试：retry={}, port={}, deviceId={}", retry, port, deviceId);
            } catch (Exception e) {
                // 重试耗尽才抛异常
                if (retry >= CUSTOM_RETRY_TIMES) {
                    log.error("Modbus重试耗尽（{}次）：ip={}, port={}, deviceId={}", CUSTOM_RETRY_TIMES, ip, port, deviceId);
                }
                log.info("Modbus读取异常，重试：retry={}, port={}, deviceId={}", retry, port, deviceId, e);
            } finally {
                // 3. 无论成功/失败，归还连接到池（关键：避免连接泄漏）
                master.destroy();
            }
        }
        log.info("Modbus读取无有效结果：ip=" + ip + ", port=" + port + ", deviceId=" + deviceId);
        return null;
    }

    // -------------------------- Modbus基础操作（保留核心，精简冗余） --------------------------
    /**
     * 创建Modbus Master（无冗余配置，仅核心参数）
     */
    private ModbusMaster createModbusMaster(String ip, int port) throws ModbusInitException {
        IpParameters params = new IpParameters();
        params.setHost(ip);
        params.setPort(port);

        TcpMaster master = (TcpMaster) modbusFactory.createTcpMaster(params, true);
        master.setTimeout(MODBUS_CONN_TIMEOUT_MS);
        master.setRetries(0); // 禁用内置重试，用自定义重试逻辑
        master.init();
        return master;
    }

    /**
     * 读取寄存器（无冗余判断，直接强转）
     */
    private int[] readRegisters(ModbusMaster master, int deviceId) throws ModbusTransportException {
        ReadHoldingRegistersRequest request = Modbus4jUtils.getReadHoldingRegistersRequest(
                deviceId, REG_START_ADDR, REG_READ_COUNT);
        ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);

        // 转换为无符号整数（单设备场景无需循环优化，直接遍历）
        short[] signedVals = response.getShortData();
        int[] unsignedVals = new int[signedVals.length];
        for (int i = 0; i < signedVals.length; i++) {
            unsignedVals[i] = signedVals[i] & 0xFFFF;
        }
        return unsignedVals;
    }

    /**
     * 销毁Modbus连接（精简反射逻辑，失败仅日志告警，不中断主流程）
     */
    private void destroyModbusMaster(ModbusMaster master, int deviceId) {
        if (master == null) return;

        // 1. 关闭底层Socket（反射失败不抛异常，避免影响主流程）
        if (master instanceof TcpMaster) {
            closeSocket((TcpMaster) master, deviceId);
        }

        // 2. 销毁Master
        try {
            master.destroy();
        } catch (Exception e) {
            log.info("ModbusMaster销毁异常：deviceId={}", deviceId, e);
        }
    }

    /**
     * 关闭底层Socket（精简反射代码，减少异常处理层级）
     */
    private void closeSocket(TcpMaster tcpMaster, int deviceId) {
        try {
            Field socketField = TcpMaster.class.getDeclaredField("socket");
            socketField.setAccessible(true);
            Socket socket = (Socket) socketField.get(tcpMaster);
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        } catch (Exception e) {
            log.info("Modbus Socket关闭异常：deviceId={}", deviceId, e);
        }
    }

    // -------------------------- 保留的基础工具方法（仅核心） --------------------------
    /**
     * 校验Modbus结果有效性（调用ResultHandler的停止条件，避免重复逻辑）
     */
    private boolean isValidResult(int[] result) {
        if (result == null || result.length == 0) {
            return false;
        }
        Predicate<int[]> stopCondition = ModbusResultHandler.createDefaultStopCondition();
        return stopCondition.test(result);
    }

    /**
     * 从JobContext获取storeyId（精简异常处理，失败返回unknown）
     */
    private Long getStoreyIdFromContext(JobExecutionContext context) {
        try {
            if (context == null) {
                log.error("getStoreyIdFromContext：context为null");
                return 0L;
            }
            JobDetail jobDetail = context.getJobDetail();
            if (jobDetail == null) {
                log.error("getStoreyIdFromContext：jobDetail为null");
                return 0L;
            }
            JobDataMap dataMap = jobDetail.getJobDataMap();
            if (dataMap == null) {
                log.error("getStoreyIdFromContext：dataMap为null");
                return 0L;
            }
            long fStoreyId = dataMap.getLong("fStoreyId");
            log.info("getStoreyIdFromContext：获取到storeyId={}", fStoreyId);
            return fStoreyId;
        } catch (Exception e) {
            log.error("getStoreyIdFromContext异常：", e);
            return 0L;
        }
    }

    /**
     * 记录告警（保留原逻辑，精简参数判断）
     */
    protected void recordAlarm(TStoreyInfo storeyInfo, String equipmentCode, String alarmData) {
        try {
            TEquipmentAlarmData alarm = new TEquipmentAlarmData();
            alarm.setfAlarmType("03");
            alarm.setfEquipmentCode(storeyInfo != null ? storeyInfo.getfStoreyCode() : equipmentCode);
            alarm.setfAlarmData(alarmData);
            alarm.setfCreateTime(new Date());
            alarmDataService.insertTEquipmentAlarmData(alarm);
        } catch (Exception e) {
            log.info("告警记录失败：{}", alarmData, e);
        }
    }

    protected void updateDeviceError(String ip) {
        int deviceId = getDeviceId(); 
        // 2. 查询数据库并校验（避免NPE）
        PalletDeviceBinding palletDeviceBinding;
        try {
            palletDeviceBinding = palletDeviceBindingMapper.selectByTrayIdAndIndex(ip, deviceId);
            if(palletDeviceBinding !=  null) {
                palletDeviceBinding.setStatus("4");
            }
        } catch (Exception e) {
            log.info("查询PalletDeviceBinding异常：ip={}, deviceId={}", ip, deviceId, e);
        }
    }
}
