package org.dromara.tcpfuwu.handler;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.dromara.shebei.domain.dto.OpsSbLiebiaoDto;
import org.dromara.shebei.domain.vo.OpsSbMbBianliangVo;
import org.dromara.shebei.service.IOpsSbLiebiaoService;
import org.dromara.tcpfuwu.constant.ModbusConstant;
import org.dromara.tcpfuwu.domain.DeviceCache;
import org.dromara.tcpfuwu.domain.ModbusVariable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 设备连接处理类：处理单设备的心跳和Modbus通信
 */
//@Component
public class DeviceHandler {
    // 配置参数
    private static final long HEARTBEAT_EXPIRE_MS = 30 * 1000;
    private static final int HEARTBEAT_LEN = 20;
    private static final int SN_CODE_LEN = 20;
    private static final int MODBUS_REQUEST_CYCLE = 10; // 请求周期（秒）

    // 依赖注入
//    @Autowired
    private final IOpsSbLiebiaoService sbLiebiaoService;

    // 设备信息
    private final Socket clientSocket;
    private final String deviceAddr;
    private final DeviceCache deviceCache;
    private final Map<String, DeviceCache> globalCache;

    // 状态控制
    private final AtomicBoolean isRunning = new AtomicBoolean(true);
    private InputStream in;
    private OutputStream out;
    private ScheduledExecutorService modbusScheduler;

    public DeviceHandler(
        Socket clientSocket,
        String deviceAddr,
        DeviceCache deviceCache,
        Map<String, DeviceCache> globalCache,
        IOpsSbLiebiaoService sbLiebiaoService // 新增：手动传递Spring服务
    ) {
        this.clientSocket = clientSocket;
        this.deviceAddr = deviceAddr;
        this.deviceCache = deviceCache;
        this.globalCache = globalCache;
        this.sbLiebiaoService = sbLiebiaoService; // 赋值
    }

    /**
     * 处理设备通信的主方法
     */
    public void handle() {
        try {
            // 初始化IO流
            in = clientSocket.getInputStream();
            out = clientSocket.getOutputStream();

            // 启动心跳接收线程
            startHeartbeatReceiver();

            // 启动Modbus定时请求线程
            startModbusScheduler();

            // 阻塞等待线程结束
            synchronized (this) {
                wait();
            }

        } catch (Exception e) {
            System.err.printf("【设备处理异常】地址：%s，原因：%s%n", deviceAddr, e.getMessage());
        } finally {
            // 清理资源
            stop();
        }
    }

    /**
     * 启动心跳接收线程
     */
    private void startHeartbeatReceiver() {
        new Thread(() -> {
            try {
                byte[] buffer = new byte[1024];
                while (isRunning.get()) {
                    int len = in.read(buffer);
                    if (len <= 0) {
                        System.out.printf("【心跳接收失败】设备：%s，连接断开%n", deviceAddr);
                        break;
                    }

                    byte[] data = Arrays.copyOf(buffer, len);
                    if (isHeartbeatData(data)) {
                        // 解析SN码
                        String snCode = new String(Arrays.copyOfRange(data, 0, data.length)).trim();
                        // 更新缓存
                        deviceCache.setSnCode(snCode);
                        deviceCache.setLastHeartbeatTime(System.currentTimeMillis());
                        System.out.printf("【心跳更新】设备：%s，SN：%s%n", deviceAddr, snCode);
                    }
                }
            } catch (Exception e) {
                System.err.printf("【心跳线程异常】设备：%s，原因：%s%n", deviceAddr, e.getMessage());
            } finally {
                // 心跳线程结束，标记设备离线
                isRunning.set(false);
                synchronized (DeviceHandler.this) {
                    DeviceHandler.this.notify();
                }
            }
        }, "heartbeat-" + deviceAddr).start();
    }

    /**
     * 启动Modbus定时请求调度器
     */
    private void startModbusScheduler() {
        modbusScheduler = Executors.newSingleThreadScheduledExecutor(
            r -> new Thread(r, "modbus-" + deviceAddr)
        );

        modbusScheduler.scheduleAtFixedRate(() -> {
            if (!isRunning.get() || !clientSocket.isConnected() || clientSocket.isClosed()) {
                stop();
                return;
            }

            // 未获取SN码则等待
            if (deviceCache.getSnCode() == null) {
                System.out.printf("【等待心跳】设备：%s%n", deviceAddr);
                return;
            }

            // 查询变量并处理
            List<ModbusVariable> variables = queryVariables(deviceCache.getSnCode());
            if (variables.isEmpty()) {
                System.out.printf("【无变量配置】SN：%s%n", deviceCache.getSnCode());
                return;
            }

            // 遍历变量发送请求
            for (ModbusVariable var : variables) {
                try {
                    handleVariable(var);
                } catch (Exception e) {
                    System.err.printf("【变量处理失败】SN：%s，变量：%s，原因：%s%n",
                            deviceCache.getSnCode(), var.getVariableName(), e.getMessage());
                }
            }

        }, 0, MODBUS_REQUEST_CYCLE, TimeUnit.SECONDS);
    }

    /**
     * 处理单个变量的请求与解析
     */
//    private void handleVariable(ModbusVariable var) throws Exception {
//        // 生成请求帧
//        byte[] request = generateModbusFrame(var);
//        System.out.printf("【发送请求】SN：%s，变量：%s，帧：%s%n",
//                deviceCache.getSnCode(), var.getVariableName(), bytesToHex(request));
//
//        // 发送请求
//        out.write(request);
//        out.flush();
//
//        // 接收响应（超时3秒）
//        byte[] response = receiveResponse(10000);
//        if (response == null) {
//            throw new Exception("响应超时");
//        }
//
//        // 验证响应
//        if (!validateResponse(response, var)) {
//            throw new Exception("响应验证失败");
//        }
//
//        // 解析数据
//        Object value = parseValue(response, var);
//        System.out.printf("【解析成功】SN：%s，变量：%s，值：%s %s%n",
//                deviceCache.getSnCode(), var.getVariableName(), value, var.getUnit());
//
//        // 更新缓存
//        deviceCache.getVariableValues().put(var.getVariableName(), value);
//    }
    private void handleVariable(ModbusVariable var) throws Exception {
        // 1. 校验设备是否在线（避免无效通信）
        if (!isDeviceOnline()) {
            throw new Exception("设备离线，无法通信");
        }

        // 2. 生成请求帧并打印详细信息
        byte[] request = generateModbusFrame(var);
        String requestHex = bytesToHex(request);
        System.out.printf("【发送请求】SN：%s，变量：%s，帧：%s，长度：%d字节%n",
            deviceCache.getSnCode(), var.getVariableName(), requestHex, request.length);

        // 3. 发送请求（确保输出流正确）
        try {
            out.write(request);
            out.flush();
            System.out.println("【发送成功】请求已提交到串口");
        } catch (IOException e) {
            throw new Exception("发送请求失败：" + e.getMessage(), e);
        }

        // 4. 接收响应（增加重试机制，最多3次）
        byte[] response = null;
        int retryCount = 0;
        while (retryCount < 3 && response == null) {
            try {
                response = receiveResponse(10000); // 每次超时10秒
                if (response == null) {
                    retryCount++;
                    System.out.printf("【响应超时】第%d次重试...%n", retryCount);
                    // 重试前短暂延迟，避免设备繁忙
                    Thread.sleep(500);
                }
            } catch (IOException e) {
                throw new Exception("接收响应失败：" + e.getMessage(), e);
            }
        }
        if (response == null) {
            throw new Exception("多次重试后仍无响应，可能设备离线或参数错误");
        }

        // 5. 打印响应原始数据（关键调试信息）
        String responseHex = bytesToHex(response);
        System.out.printf("【收到响应】长度：%d字节，帧：%s%n", response.length, responseHex);

        // 6. 验证响应
        if (!validateResponse(response, var)) {
            throw new Exception("响应验证失败，原始帧：" + responseHex);
        }

        // 7. 解析数据（后续步骤不变）
        Object value = parseValue(response, var);
        System.out.printf("【解析成功】SN：%s，变量：%s，值：%s %s%n",
            deviceCache.getSnCode(), var.getVariableName(), value, var.getUnit());

        // 8. 更新缓存
        deviceCache.getVariableValues().put(var.getVariableName(), value);
    }

    // 辅助方法：判断设备是否在线（如通过ping或前次通信时间）
    private boolean isDeviceOnline() {
        // 实现逻辑：如检查最近一次成功通信时间是否在有效期内
        return true;
    }

    /**
     * 从数据库查询变量列表
     */
    private List<ModbusVariable> queryVariables(String snCode) {
        ArrayList<ModbusVariable> modbusVariables = new ArrayList<>();
        OpsSbLiebiaoDto opsSbLiebiaoDto = sbLiebiaoService.getLiebiaoBianliangList(snCode);
        if (!opsSbLiebiaoDto.getSbMbBianliangVos().isEmpty()) {
            for (OpsSbMbBianliangVo v : opsSbLiebiaoDto.getSbMbBianliangVos()) {
                ModbusVariable modbusVariable = new ModbusVariable();
                modbusVariable.setDataType(v.getShujvGeshi());
                modbusVariable.setVariableName(v.getBlName());
                modbusVariable.setUnit(v.getBlDanwei());
                modbusVariable.setSnCode(snCode);
                modbusVariable.setSlaveId(opsSbLiebiaoDto.getSlaveId().intValue());
                String pureHex = v.getJicunqiGnm().substring(2);
                modbusVariable.setFuncCode(Integer.parseInt(pureHex,16));
                modbusVariable.setStartRegAddr(Integer.parseInt(v.getJicunqiAdd()));
                switch (v.getShujvGeshi()) {
                    case "S16": modbusVariable.setRegQuantity(1); break;
                    case "U16": modbusVariable.setRegQuantity(1); break;
                    case "S32": modbusVariable.setRegQuantity(2); break;
                    case "U32": modbusVariable.setRegQuantity(2); break;
                }
                modbusVariables.add(modbusVariable);
            }
        }
        return modbusVariables;
    }

    /**
     * 停止所有资源
     */
    public void stop() {
        isRunning.set(false);

        // 关闭Modbus调度器
        if (modbusScheduler != null) {
            modbusScheduler.shutdown();
        }

        // 关闭Socket和流
        try {
            if (in != null) in.close();
            if (out != null) out.close();
            if (clientSocket != null && !clientSocket.isClosed()) {
                clientSocket.close();
            }
        } catch (Exception e) {
            System.err.printf("【资源关闭异常】设备：%s，原因：%s%n", deviceAddr, e.getMessage());
        }

        // 从全局缓存移除
        globalCache.remove(deviceAddr);
        System.out.printf("【设备下线】地址：%s%n", deviceAddr);
    }

    // ------------------------------ 工具方法 ------------------------------
    private boolean isHeartbeatData(byte[] data) {
        if (data.length != HEARTBEAT_LEN) return false;
        for (byte b : data) {
            if (b < '0' || b > '9') return false;
        }
        return true;
    }

    private byte[] generateModbusFrame(ModbusVariable var) {
        ByteBuffer buffer = ByteBuffer.allocate(6).order(ByteOrder.BIG_ENDIAN);
        buffer.put(var.getSlaveId().byteValue())
              .put(var.getFuncCode().byteValue())
              .putShort(var.getStartRegAddr().shortValue())
              .putShort(var.getRegQuantity().shortValue());

        byte[] body = buffer.array();
        byte[] crc = calculateCrc16(body);

        byte[] frame = new byte[body.length + crc.length];
        System.arraycopy(body, 0, frame, 0, body.length);
        System.arraycopy(crc, 0, frame, body.length, crc.length);
        return frame;
    }

    private byte[] receiveResponse(int timeoutMs) throws Exception {
        long start = System.currentTimeMillis();
        while (System.currentTimeMillis() - start < timeoutMs) {
            if (in.available() > 0) {
                byte[] buffer = new byte[1024];
                int len = in.read(buffer);
                return Arrays.copyOf(buffer, len);
            }
            Thread.sleep(10);
        }
        return null;
    }

    private boolean validateResponse(byte[] response, ModbusVariable var) {
        if (response.length < 5) return false;
        if (response[0] != var.getSlaveId() || response[1] != var.getFuncCode()) return false;
        if (response[2] != var.getRegQuantity() * 2) return false;

        byte[] body = Arrays.copyOf(response, response.length - 2);
        byte[] receivedCrc = Arrays.copyOfRange(response, response.length - 2, response.length);
        return Arrays.equals(receivedCrc, calculateCrc16(body));
    }

//    private Object parseValue(byte[] response, ModbusVariable var) {
//        ByteBuffer buffer = ByteBuffer.wrap(response, 3, response[2]).order(ByteOrder.BIG_ENDIAN);
//        double rawValue;
//
//        switch (var.getDataType().toUpperCase()) {
//            case "S16": rawValue = buffer.getShort(); break;
//            case "U16": rawValue = buffer.getShort() & 0xFFFF; break;
//            case "S32": rawValue = buffer.getInt(); break;
//            case "U32": rawValue = buffer.getLong() & 0xFFFFFFFFL; break;
//            case "FLOAT": rawValue = buffer.getFloat(); break;
//            default: throw new IllegalArgumentException("不支持的数据类型：" + var.getDataType());
//        }
//
//        return rawValue * var.getMultiplier();
//    }

    private Object parseValue(byte[] response, ModbusVariable var) {
        // 1. 初始化缓冲区：从响应报文的"数据部分"开始读取（跳过前3字节：从站地址、功能码、字节计数）
        // 字节序设置为BIG_ENDIAN（大端序），符合"高字节在前、高字在前"的规则
        ByteBuffer buffer = ByteBuffer.wrap(response, 3, response[2]).order(ByteOrder.BIG_ENDIAN);
        double rawValue; // 用double统一存储原始值，兼容整数和后续倍率计算

        switch (var.getDataType().toUpperCase()) {
            case "U16":
                // 读取2字节（高字节在前），转换为无符号整数
                // getShort()返回有符号short，&0xFFFF转换为无符号（0~65535）
                rawValue = buffer.getShort() & 0xFFFF;
                break;
            case "S16":
                // 读取2字节（高字节在前），直接按有符号short解析（-32768~32767）
                rawValue = buffer.getShort();
                break;
            case "U32":
                // 读取4字节（高字在前，每个字内高字节在前），转换为无符号整数
                // getInt()返回有符号int，转换为long后&0xFFFFFFFFL得到无符号值（0~4294967295）
                rawValue = (long) buffer.getInt() & 0xFFFFFFFFL;
                break;
            case "S32":
                // 读取4字节（高字在前，每个字内高字节在前），直接按有符号int解析
                rawValue = buffer.getInt();
                break;
            case "FLOAT":
                // 单精度浮点型（32位），大端序解析（符合IEEE 754标准）
                rawValue = buffer.getFloat();
                break;
            default:
                throw new IllegalArgumentException("不支持的数据类型：" + var.getDataType());
        }

        // 原始值 × 倍率 = 实际物理量（如温度、压力等）
        return rawValue * var.getMultiplier();
    }

    private byte[] calculateCrc16(byte[] data) {
        int crc = 0xFFFF;
        for (byte b : data) {
            crc = (crc >> 8) ^ ModbusConstant.CRC16_TABLE[(crc ^ (b & 0xFF)) & 0xFF];
        }
        return new byte[]{(byte) (crc & 0xFF), (byte) ((crc >> 8) & 0xFF)};
    }

    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }
}
