package com.zksy.radar.utils;

import com.zksy.common.exception.InvalidMessageException;
import com.zksy.radar.domain.RadarData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.Arrays;

public class DataParser {

    private static final Logger logger = LoggerFactory.getLogger(DataParser.class);

    /**
     * 平升6216协议完整校验：
     * 1. 长度检查
     * 2. 系统识别码检查
     * 3. 异或校验（外层 PS 协议）
     * 4. CRC16 校验（内层 历史记录协议）
     */
    public static void validateMessage(byte[] msgBytes) throws InvalidMessageException {
        // 1. 长度检查
        if (msgBytes == null || msgBytes.length < 10) {
            throw new InvalidMessageException("数据帧长度过短");
        }

        // 2. 帧头与系统识别码检查
        if (msgBytes[0] != 0x12 || msgBytes[1] != 0x34 || msgBytes[2] != 0x56) {
            throw new InvalidMessageException("系统识别码错误，应为 12 34 56");
        }

        // 3. 校验整帧长度（从系统识别码到最后1字节）
        int declaredLen = ((msgBytes[3] & 0xFF) << 8) | (msgBytes[4] & 0xFF);
        if (declaredLen != msgBytes.length) {
            throw new InvalidMessageException(String.format(
                    "帧长度不匹配：声明=%d 实际=%d", declaredLen, msgBytes.length));
        }

        // 4. 异或校验（最后一字节）
        byte receivedXor = msgBytes[msgBytes.length - 1];
        byte[] xorData = Arrays.copyOfRange(msgBytes, 0, msgBytes.length - 1);
        byte calculatedXor = ProtocolUtils.calculateXorCheck(xorData);
        if (receivedXor != calculatedXor) {
            throw new InvalidMessageException(String.format(
                    "异或校验失败：计算=0x%02X 接收=0x%02X", calculatedXor, receivedXor));
        }

        // 5. 仅对上报历史记录帧(0x31)进行 CRC 校验
        byte frameType = msgBytes[6];
        if (frameType == 0x31) {
            // CRC 高低字节在帧末尾的倒数第3、第2字节（倒数第1是异或）
            int crcEndIndex = msgBytes.length - 3;
            int crcHighIndex = crcEndIndex;
            int crcLowIndex = crcEndIndex + 1;

            if (crcHighIndex <= 0) {
                throw new InvalidMessageException("CRC数据段索引错误");
            }

            // 设备编码开始（内层协议起点）：
            // 外层结构 3+2+1+1+1+n+1+m => 源地址/目的地址结束后紧接设备编码(0x01)
            // 简化方式：找到第一个 0x01 2C 序列即可（设备编码 + 功能码）
            int deviceCodeIndex = -1;
            for (int i = 0; i < msgBytes.length - 1; i++) {
                if (msgBytes[i] == 0x01 && msgBytes[i + 1] == 0x2C) {
                    deviceCodeIndex = i;
                    break;
                }
            }

            if (deviceCodeIndex == -1) {
                throw new InvalidMessageException("未找到设备编码起点 (0x01 2C)");
            }

            byte[] crcData = Arrays.copyOfRange(msgBytes, deviceCodeIndex, crcEndIndex);
            int calculatedCrc = ProtocolUtils.calculateCRC16(crcData);
            int receivedCrc = ((msgBytes[crcLowIndex] & 0xFF) << 8) | (msgBytes[crcHighIndex] & 0xFF);

            if (calculatedCrc != receivedCrc) {
                throw new InvalidMessageException(String.format(
                        "CRC校验失败：计算=0x%04X 接收=0x%04X", calculatedCrc, receivedCrc));
            }
        }

        logger.debug("消息通过所有校验");
    }

    /**
     * 解析消息（支持历史记录帧0x31和结束通讯帧0x34，协议2.2.3）
     */
    public static RadarData parseMessage(byte[] msgBytes) {
        RadarData data = new RadarData();
        int index = 0;

        // 1. 系统识别码
        data.setSystemIdentifier(String.format("%02X%02X%02X", msgBytes[0], msgBytes[1], msgBytes[2]));
        index += 3;

        // 2. 帧长度
        int frameLength = ((msgBytes[index] & 0xFF) << 8) | (msgBytes[index + 1] & 0xFF);
        data.setFrameLength(String.valueOf(frameLength));
        index += 2;

        // 3. 包序号、帧类型
        data.setPacketSequence(String.format("%02X", msgBytes[index++]));
        byte frameType = msgBytes[index++];
        data.setDataType(String.format("0x%02X", frameType));

        // 4. 源地址：按规则计算长度（原地址标识转10进制，奇数+1后除2）
        int srcLenFlag = msgBytes[index++] & 0xFF; // 源地址标识（10进制）
        int srcAddrSize = calculateAddrLength(srcLenFlag); // 计算源地址字节长度
        srcAddrSize = Math.min(srcAddrSize, 10); // 防止越界，最多10字节
        byte[] srcAddr = Arrays.copyOfRange(msgBytes, index, index + srcAddrSize);
        data.setSourceAddr(parseBcdToStr(srcAddr));
        index += srcAddrSize;

        // 5. 目的地址：按相同规则计算长度
        int dstLenFlag = msgBytes[index++] & 0xFF; // 目的地址标识（10进制）
        int dstAddrSize = calculateAddrLength(dstLenFlag); // 计算目的地址字节长度
        dstAddrSize = Math.min(dstAddrSize, 10); // 防止越界，最多10字节
        byte[] dstAddr = Arrays.copyOfRange(msgBytes, index, index + dstAddrSize);
        data.setDestAddr(parseBcdToStr(dstAddr));
        data.setDestAddrLength(String.valueOf(dstAddrSize));
        index += dstAddrSize;

        // 6. 帧类型 = 0x31
        if (frameType == 0x31) {
            // 设备ID
            data.setDeviceCode(String.format("%02X", msgBytes[index++]));
            // 功能码
            data.setFunctionCode(String.format("%02X", msgBytes[index++]));

            // 预留6字节（原代码拆成了4+2，合并为6字节）
            data.setReserve1(String.format("%02X%02X%02X%02X%02X%02X",
                    msgBytes[index], msgBytes[index+1], msgBytes[index+2],
                    msgBytes[index+3], msgBytes[index+4], msgBytes[index+5]));
            index += 6;

            // 记录数量
            int recordCount = msgBytes[index++] & 0xFF;
            data.setRecordCount(String.valueOf(recordCount));

            // 记录格式
            int recordFormat = ((msgBytes[index] & 0xFF) << 8) | (msgBytes[index + 1] & 0xFF);
            data.setRecordFormat(String.format("%04X", recordFormat));
            index += 2;

            // 电池电压（整型转浮点，除以100）
            int powerVolt = ((msgBytes[index] & 0xFF) << 8) | (msgBytes[index + 1] & 0xFF);
            data.setPowerVoltage(String.format("%.2f", powerVolt / 100.0));
            index += 2;

            // 现场状态（解析D3位判断是否最后一包）
            byte fieldStatus = msgBytes[index++];
            data.setFieldStatus(String.format("%02X", fieldStatus));
            // D3位：0x08 对应二进制 00001000，按位与判断
            data.setIsLastPacket((fieldStatus & 0x08) != 0);

            // 协议版本（两个字节）
            data.setProtocolVersion(String.format("%02X", msgBytes[index++]));
            data.setParamVersion(String.format("%02X", msgBytes[index++]));
            // 信号质量
            data.setSignalQuality(String.valueOf(msgBytes[index++] & 0xFF));

            // 预留3字节
            data.setReserve3(String.format("%02X%02X%02X", msgBytes[index], msgBytes[index + 1], msgBytes[index + 2]));
            index += 3;

            // --- 历史记录部分 ---
            if (recordCount > 0 && index + 4 <= msgBytes.length) {
                // 数据采集时间（4字节）
                int ts = ((msgBytes[index] & 0xFF) << 24) | ((msgBytes[index + 1] & 0xFF) << 16)
                        | ((msgBytes[index + 2] & 0xFF) << 8) | (msgBytes[index + 3] & 0xFF);
                LocalDateTime recordTime = ProtocolUtils.convertToDateTime(ts);
                data.setTimestampSince(recordTime);
                index += 4;

                // 表1净累计（4字节浮点）
                float netTotal = bytesToFloat(msgBytes, index);
                index += 4;
                // 表1瞬时流量（4字节浮点）
                float instantFlow = bytesToFloat(msgBytes, index);
                index += 4;
                // 流速（4字节浮点）
                float flowSpeed = bytesToFloat(msgBytes, index);
                index += 4;
                // 水位（4字节浮点）
                float waterLevel = bytesToFloat(msgBytes, index);
                index += 4;
                // 开关量（4字节整型）
                long switchVal = bytesToUInt32(msgBytes, index);
                index += 4;

                // 设置浮点型数据（保留两位小数展示）
                data.setMeter1NetTotal(String.format("%.2f", netTotal));
                data.setMeter1InstantFlow(String.format("%.2f", instantFlow));
                data.setFlowSpeed(String.format("%.2f", flowSpeed));
                data.setWaterLevel(String.format("%.2f", waterLevel));
                data.setSwitchValue(String.format("%08X", switchVal));

                // 格式化历史记录字符串
                data.setHistoryRecords(String.format(
                        "时间:%s,净累计:%.2f,瞬时流量:%.2f,流速:%.2f,水位:%.2f,开关量:%08X",
                        recordTime, netTotal, instantFlow, flowSpeed, waterLevel, switchVal));
            }
        }

        data.setCreateTime(LocalDateTime.now());
        return data;
    }

    /**
     * 计算地址字节长度：原地址标识转10进制，奇数+1后除2
     * @param lenFlag 地址标识（10进制值）
     * @return 地址字节长度
     */
    private static int calculateAddrLength(int lenFlag) {
        int temp = lenFlag;
        // 奇数则+1
        if (temp % 2 != 0) {
            temp += 1;
        }
        // 除2得到字节长度
        return temp / 2;
    }

    /**
     * 4字节（高字节在前）转单精度浮点型（IEEE 754）
     * @param data 字节数组
     * @param offset 起始偏移量
     * @return 单精度浮点值
     */
    private static float bytesToFloat(byte[] data, int offset) {
        // 先将4字节转为int（高字节在前）
        int intValue = ((data[offset] & 0xFF) << 24)
                | ((data[offset + 1] & 0xFF) << 16)
                | ((data[offset + 2] & 0xFF) << 8)
                | (data[offset + 3] & 0xFF);
        // 按IEEE 754规则转换为float
        return Float.intBitsToFloat(intValue);
    }

    /** 工具方法：BCD转字符串 */
    private static String parseBcdToStr(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }

    /** 工具方法：4字节无符号整型 */
    private static long bytesToUInt32(byte[] data, int offset) {
        return ((long) (data[offset] & 0xFF) << 24)
                | ((long) (data[offset + 1] & 0xFF) << 16)
                | ((long) (data[offset + 2] & 0xFF) << 8)
                | ((long) (data[offset + 3] & 0xFF));
    }
}