package com.evobms.project.bms.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class GBT32960FullDecoder {
    private static final Logger log = LoggerFactory.getLogger(GBT32960FullDecoder.class);

    // 从SN码生成AES密钥
    private static byte[] generateKeyFromSn(String sn) {
        String s = sn == null ? "" : sn;
        String key;
        if ("EVO0001".equals(s)) {
            StringBuilder sb = new StringBuilder(16);
            sb.append("EVO0001");
            while (sb.length() < 16) sb.append('0');
            key = sb.toString();
        } else if (s.length() >= 16) {
            key = s.substring(s.length() - 16);
        } else {
            StringBuilder sb = new StringBuilder(16);
            sb.append(s);
            while (sb.length() < 16) sb.append('0');
            key = sb.toString();
        }
        return key.getBytes(StandardCharsets.UTF_8);
    }

    // AES解密
    private static byte[] decryptData(byte[] encryptedData, byte[] key) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        return cipher.doFinal(encryptedData);
    }

    // BCC校验
    private static byte calculateBCC(byte[] data, int start, int end) {
        byte bcc = 0;
        for (int i = start; i < end; i++) {
            bcc ^= data[i];
        }
        return bcc;
    }

    // 16进制字符串转字节数组
    private static byte[] hexStringToByteArray(String hex) {
        hex = hex.replaceAll("\\s", ""); // 移除空格
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    // 字节数组转16进制字符串
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }

    // 详细的协议解析方法
     public static String  parseGBT32960Data(byte[] receivedData, String deviceSn) {
         String bytesData = null;
         try {
             log.info("=== GBT32960协议数据解析 ===");
             log.info("实际数据长度: " + receivedData.length + " 字节");
             // 1. 检查起始位
             if (receivedData.length < 2 || receivedData[0] != 0x23 || receivedData[1] != 0x23) {
                 log.error("无效的起始位或数据过短");
                 return null;
             }
             log.info("起始位: 0x2323 ✓");

             // 2. 打印完整数据用于调试
             log.info("完整数据: " + bytesToHex(receivedData));

             // 3. 分析协议结构
             int offset = 2; // 跳过起始位

             // 命令标识
             byte commandId = receivedData[offset++];
             log.info("命令标识: 0x" + String.format("%02X", commandId));

             // 应答标志
             byte responseFlag = receivedData[offset++];
             log.info("应答标志: 0x" + String.format("%02X", responseFlag));

             // VIN码 (17字节)
             byte[] vin = Arrays.copyOfRange(receivedData, offset, offset + 17);
             offset += 17;
             log.info("VIN码: " + bytesToHex(vin) + " (" + new String(vin).trim() + ")");

             // 加密方式
             byte encryptionMethod = receivedData[offset++];
             log.info("加密方式: 0x" + String.format("%02X", encryptionMethod));

             if (encryptionMethod != 0x03) {
                 log.error("不支持的解密方式，期望AES128(0x03)");
                 return null;
             }

             // 数据单元长度 (WORD, 大端序)
             int dataUnitLength = ((receivedData[offset] & 0xFF) << 8) | (receivedData[offset + 1] & 0xFF);
             offset += 2;
             log.info("声明的数据单元长度: " + dataUnitLength + " 字节");

             // 计算当前头部长度
             int headerLength = offset;
             log.info("协议头部长度: " + headerLength + " 字节");

             // 剩余数据长度
             int remainingDataLength = receivedData.length - headerLength;
             log.info("剩余数据长度: " + remainingDataLength + " 字节");

             // 4. 重新计算期望长度
             // 方案1: 如果BCC是1字节
             int expectedLength1 = headerLength + dataUnitLength + 1;
             // 方案2: 如果BCC是2字节（根据您最初描述）
             int expectedLength2 = headerLength + dataUnitLength + 2;

             log.info("期望长度 (BCC=1字节): " + expectedLength1);
             log.info("期望长度 (BCC=2字节): " + expectedLength2);
             log.info("实际长度: " + receivedData.length);

             // 5. 确定正确的数据范围
             int encryptedDataStart = offset;
             int encryptedDataEnd;
             int bccStart;

             if (receivedData.length == expectedLength1) {
                 // BCC为1字节
                 encryptedDataEnd = encryptedDataStart + dataUnitLength;
                 bccStart = encryptedDataEnd;
                 log.info("采用方案: BCC为1字节");
             } else if (receivedData.length == expectedLength2) {
                 // BCC为2字节
                 encryptedDataEnd = encryptedDataStart + dataUnitLength;
                 bccStart = encryptedDataEnd;
                 log.info("采用方案: BCC为2字节");
             } else {
                 // 自动适应：使用剩余的所有数据作为加密数据
                 encryptedDataEnd = receivedData.length - 1; // 假设BCC为1字节
                 int actualDataUnitLength = encryptedDataEnd - encryptedDataStart;
                 log.info("长度不匹配，使用实际数据单元长度: " + actualDataUnitLength + " 字节");
                 dataUnitLength = actualDataUnitLength;
                 bccStart = encryptedDataEnd;
             }

             // 6. 提取加密数据
             byte[] encryptedData = Arrays.copyOfRange(receivedData, encryptedDataStart, encryptedDataStart + dataUnitLength);
             log.info("加密数据长度: " + encryptedData.length + " 字节");
             log.info("加密数据 (前64字节): " + bytesToHex(Arrays.copyOf(encryptedData, Math.min(64, encryptedData.length))) + "...");
             if (encryptedData.length % 16 != 0) {
                 log.error("AES密文长度非法，不是16倍数，length=" + encryptedData.length);
                 return null;
             }
             // 7. 提取BCC校验码
             int bccLength = receivedData.length - (encryptedDataStart + dataUnitLength);
             byte[] receivedBCC = Arrays.copyOfRange(receivedData, encryptedDataStart + dataUnitLength, receivedData.length);
             //log.info("BCC长度: " + bccLength + " 字节");
             //log.info("接收到的BCC: " + bytesToHex(receivedBCC));

             // 8. 计算BCC校验范围：从命令单元开始到加密数据结束
             int bccCalcStart = 2; // 从命令标识开始 (跳过0x2323)
             int bccCalcEnd = encryptedDataStart + dataUnitLength;
             byte calculatedBCC = calculateBCC(receivedData, bccCalcStart, bccCalcEnd);

             log.info("BCC计算范围: 字节[" + bccCalcStart + "~" + bccCalcEnd + ")");
             log.info("计算出的BCC: 0x" + String.format("%02X", calculatedBCC));

             // 验证BCC (只比较第一个字节)
             if (receivedBCC.length > 0 && calculatedBCC == receivedBCC[0]) {
                 log.info("BCC校验通过 ✓");
             } else {
                 log.error("BCC校验失败!");
             }


             //使用的AES密钥
             byte[] aesKey = generateKeyFromSn(deviceSn);
             System.out.println(Arrays.toString(aesKey));
             System.out.println(aesKey.length);
             log.info("AES密钥: " + bytesToHex(aesKey));
             byte[] decryptedData = decryptData(encryptedData, aesKey);
             bytesData = bytesToHex(decryptedData);
             log.info("解密成功!");
             log.info("解密后数据长度: " + decryptedData.length + " 字节");
             log.info("解密后数据 (完整): " + bytesData);


             // 10. 解析解密后的数据单元
             // parseDecryptedDataUnit(decryptedData);

         } catch (Exception e) {
             log.error("解析过程中发生错误: " + e.getMessage());
             e.printStackTrace();
         }
         return bytesData;
     }

   /* public static String parseGBT32960Data(byte[] frame, String deviceSn) throws Exception {

        if (frame == null || frame.length < 25) {
            throw new IllegalArgumentException("报文长度非法");
        }
        int offset = 0;
        // 1. 起始符校验 0x23 0x23

        if (frame[offset++] != 0x23 || frame[offset++] != 0x23) {
            throw new IllegalArgumentException("非法起始位，非0x2323");
        }
        // 2. 命令标识
        byte commandId = frame[offset++];
        // 3. 应答标志
        byte responseFlag = frame[offset++];
        // 4. VIN (17字节)
        byte[] vinBytes = Arrays.copyOfRange(frame, offset, offset + 17);
        String vin = new String(vinBytes).trim();
        offset += 17;
        // 5. 加密方式
        byte encryptType = frame[offset++];
        // 6. 数据单元长度 (大端)
        if (offset + 2 > frame.length) {
            throw new IllegalArgumentException("数据单元长度字段越界");
        }
        int dataUnitLength = ((frame[offset] & 0xFF) << 8) | (frame[offset + 1] & 0xFF);
        offset += 2;
        int headerLength = offset;

        // 7. 校验整体长度
        // 标准格式：
        // 头部 + 数据单元 + 1字节BCC
        int expectedLength = headerLength + dataUnitLength + 1;
        if (frame.length != expectedLength) {
            throw new IllegalArgumentException("报文长度不匹配，声明长度=" + dataUnitLength + "，实际长度=" + frame.length + "，期望总长度=" + expectedLength);
        }
        // 8. 提取数据单元
        byte[] dataUnit = Arrays.copyOfRange(frame, headerLength, headerLength + dataUnitLength
        );
        // 9. BCC 校验
        // BCC范围：从命令标识开始
        byte receivedBcc = frame[frame.length - 1];
        byte calculatedBcc = calculateBCC(frame, 2, headerLength + dataUnitLength
        );
        if (receivedBcc != calculatedBcc) {
            throw new IllegalStateException(
                    String.format("BCC校验失败，收到=0x%02X 计算=0x%02X",
                            receivedBcc, calculatedBcc)
            );
        }


        // 10. 加密处理

        if (encryptType == 0x01) {
            // 不加密
            return bytesToHex(dataUnit);
        }

        if (encryptType != 0x03) {
            throw new UnsupportedOperationException(
                    "不支持的加密方式: " + encryptType);
        }

        // AES 必须 16倍数
        if (dataUnitLength % 16 != 0) {
            throw new IllegalStateException(
                    "AES密文长度非法，不是16倍数，length=" + dataUnitLength);
        }


        // 11. AES解密

        byte[] aesKey = generateKeyFromSn(deviceSn);

        byte[] decrypted = decryptData(dataUnit, aesKey);

        return bytesToHex(decrypted);
    }
*/

    private static void parseDecryptedDataUnit(byte[] decryptedData) {
        log.info("\n=== 数据单元解析 ===");

        if (decryptedData == null || decryptedData.length == 0) {
            log.info("解密数据为空");
            return;
        }

        log.info("数据单元总长度: " + decryptedData.length + " 字节");
        log.info("数据单元内容: " + bytesToHex(decryptedData));

        // 尝试解析采集时间(前6字节BCD)
        if (decryptedData.length >= 6) {
            String ts = parseTimestampFromBCD(Arrays.copyOfRange(decryptedData, 0, 6));
            log.info("采集时间: " + ts + " (BCD)");
        }

        // 扫描数据块: [类型ID(1)] [长度(1)]
        int offset = 6; // 时间之后
        int itemIndex = 0;
        while (offset + 2 <= decryptedData.length) {
            int typeId = decryptedData[offset] & 0xFF;
            int length = decryptedData[offset + 1] & 0xFF;
            int payloadStart = offset + 2;
            int payloadEnd = payloadStart + length;

            // 长度异常时，尝试步进1字节继续
            if (payloadEnd > decryptedData.length) {
                // 可能当前字节并非数据块起点（例如信息标志位），小步扫描跳过
                offset += 1;
                continue;
            }

            byte[] payload = Arrays.copyOfRange(decryptedData, payloadStart, payloadEnd);
            log.info(String.format("数据块[%d] 类型: %s, 长度: %d, 起止: %d-%d", itemIndex++, typeName(typeId), length, payloadStart, payloadEnd - 1));
            log.info("数据块HEX: " + bytesToHex(Arrays.copyOf(payload, Math.min(64, payload.length))) + (payload.length > 64 ? "..." : ""));

            // 极值数据尝试详细解析
            if (typeId == 0x06) {
                try {
                    GBT32960ExtremeDataParser.ExtremeData extreme = GBT32960ExtremeDataParser.parseExtremeData(Arrays.copyOfRange(decryptedData, offset, payloadEnd), 0);
                    if (extreme != null) {
                        log.info("极值数据解析结果: " + extreme.toString());
                    }
                } catch (Exception ex) {
                    log.warn("极值数据解析异常: " + ex.getMessage());
                }
            }

            offset = payloadEnd;
        }

        // 兼容旧的极值扫描逻辑
        GBT32960ExtremeDataExtractor.ExtremeData andParseExtremeData = GBT32960ExtremeDataExtractor.findAndParseExtremeData(decryptedData);
        if (andParseExtremeData != null) {
            log.info(andParseExtremeData.toString());
        } else {
            log.info("未找到极值数据或解析失败");
        }
    }

    /**
     * 跳过数据项
     */
    private static int skipDataItem(byte[] data, int offset) {
        if (offset >= data.length) return offset;

        offset++; // 跳过数据类型
        if (offset < data.length) {
            byte length = data[offset];
            offset += 1 + (length & 0xFF); // 跳过长度字节和数据内容
        }

        return offset;
    }

    private static String typeName(int typeId) {
        switch (typeId & 0xFF) {
            case 0x01:
                return "整车数据";
            case 0x02:
                return "驱动电机数据";
            case 0x03:
                return "燃料电池数据";
            case 0x04:
                return "发动机数据";
            case 0x05:
                return "车辆位置数据";
            case 0x06:
                return "极值数据";
            case 0x07:
                return "报警数据";
            case 0x08:
                return "储能装置电压数据";
            case 0x09:
                return "储能装置温度数据";
            default:
                return String.format("未知类型(0x%02X)", typeId);
        }
    }

    // BCD字节转十进制整数
    private static int bcdToInt(byte b) {
        return ((b >> 4) & 0x0F) * 10 + (b & 0x0F);
    }

    // 解析采集时间(BCD YY MM DD hh mm ss)
    private static String parseTimestampFromBCD(byte[] data) {
        if (data == null || data.length < 6) return "未知";
        int year = 2000 + bcdToInt(data[0]);
        int month = bcdToInt(data[1]);
        int day = bcdToInt(data[2]);
        int hour = bcdToInt(data[3]);
        int minute = bcdToInt(data[4]);
        int second = bcdToInt(data[5]);
        return String.format("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
    }
}
