package com.ruoyi.device.mqtt.handler.decoder;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.device.mqtt.handler.HandlerManager;
import com.ruoyi.device.mqtt.util.CRC16Standard;
import com.ruoyi.device.mqtt.util.MsgHandlerUtil;
import com.ruoyi.device.mqtt.vo.CommonHeader;
import com.ruoyi.device.mqtt.vo.CommonTopic;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import jakarta.annotation.Resource;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 线程池异步消费 + try/catch，避免消息处理异常影响 Paho 订阅线程。
 *
 * @author lwm
 */
@Component
public class MessageHandler
{
    private static final Logger log = LoggerFactory.getLogger(MessageHandler.class);

    @Resource
    private HandlerManager handlerManager;

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 订阅消息处理
     *
     * @param topic topic主题
     * @param message 消息体
     */
    public void handler(String topic, MqttMessage message)
    {
        threadPoolTaskExecutor.execute(() -> {
            try
            {
                if (StringUtils.isEmpty(topic) || message == null)
                {
                    return;
                }
                // 1、解析topic
                CommonTopic commonTopic = parseTopic(topic);
                if (commonTopic == null)
                {
                    return;
                }
                // 2、解析 payload
                byte[] payload = message.getPayload();
                if (!verifyCrc16(payload))
                {
                    return;
                }
                ByteBuf byteBuf = Unpooled.wrappedBuffer(payload);
                // 3、读取消息头
                CommonHeader commonHeader = readHeader(byteBuf);
                // 4、转换消息体 打印日志
                String payloadHex = bytesToHexLog(payload);
                String firstType = Integer.toHexString(commonHeader.getFirstType());
                String secondType = Integer.toHexString(commonHeader.getSecondType());
                log.info("设备SN:{},设备类型:{},一级分类:{},二级分类:{},上报的数据包:{}", commonTopic.getDeviceSn(),
                    commonTopic.getDeviceType(), firstType, secondType, payloadHex);
                // 5、获取消息类型以及对应的解析器
                String key = MsgHandlerUtil.getDecoderKey(commonHeader.getFirstType(), commonHeader.getSecondType());
                IDecoder decoder = handlerManager.getDecoder(key);
                if (decoder == null)
                {
                    log.info("未找到对应业务解析器，消息类型：{}-{}", firstType, secondType);
                    return;
                }
                // 6、处理消息
                boolean result = decoder.process(commonTopic, commonHeader, byteBuf);
                log.info("订阅消息处理结果,topic={},messageid={},result={}", topic, message.getId(), result);
            }
            catch (Exception e)
            {
                log.info("消息处理异常，异常原因：{}", e.getMessage(), e);
            }
        });
    }

    /**
     * 消息头解析
     *
     * @param byteBuf byteBuf
     * @return 消息头
     */
    private CommonHeader readHeader(ByteBuf byteBuf)
    {
        CommonHeader commonHeader = new CommonHeader();
        // 固定帧头
        commonHeader.setFrameHeader(byteBuf.readUnsignedShortLE());
        // 版本号
        commonHeader.setProtoVer(byteBuf.readUnsignedByte());
        // 消息编号
        commonHeader.setMsgId(byteBuf.readUnsignedIntLE());
        // 一级分类
        commonHeader.setFirstType(byteBuf.readUnsignedByte());
        // 二级分类
        commonHeader.setSecondType(byteBuf.readUnsignedShortLE());
        // 报文消息体body长度 + 2
        commonHeader.setMsgLength(byteBuf.readUnsignedShortLE());
        return commonHeader;
    }

    /**
     * 与 {@link com.ruoyi.device.mqtt.handler.encoder.AbstractEncoder} 下行一致：整包除末尾 2 字节 CRC 外参与 CRC16 计算，
     * 再与 payload 最后 2 字节比较。
     */
    private boolean verifyCrc16(byte[] payload)
    {
        if (payload == null || payload.length < 14)
        {
            log.info("CRC16 校验失败: 报文过短, len={}", payload == null ? 0 : payload.length);
            return false;
        }
        byte[] dataWithoutTailCrc = Arrays.copyOfRange(payload, 0, payload.length - 2);
        byte[] computed = CRC16Standard.getCRCBytes(dataWithoutTailCrc);
        byte low = payload[payload.length - 2];
        byte high = payload[payload.length - 1];
        if (computed[0] != low || computed[1] != high)
        {
            log.info("CRC16 校验失败: expect [{}, {}], actual [{}, {}], hexLog={}",
                computed[0] & 0xFF, computed[1] & 0xFF, low & 0xFF, high & 0xFF,
                bytesToHexLog(payload));
            return false;
        }
        return true;
    }

    /**
     * 解析 topic
     *
     * @param topic topic
     * @return CommonTopic
     */
    private CommonTopic parseTopic(String topic)
    {
        String[] split = topic.split("/");
        if (split.length != 4)
        {
            return null;
        }
        String deviceType = split[2];
        Long deviceSn = Long.parseLong(split[3]);
        return new CommonTopic(deviceType, deviceSn);
    }

    /**
     * byte[] 转16进制 打印日志
     *
     * @param bytes bytes
     * @return 16进制字符串 打印日志
     */
    public static String bytesToHexLog(byte[] bytes)
    {
        try
        {
            StringBuilder sb = new StringBuilder();
            String tmp;
            sb.append("[");
            for (byte b : bytes)
            {
                tmp = Integer.toHexString(0xFF & b);
                if (tmp.length() == 1)
                {
                    tmp = "0" + tmp;
                }
                sb.append(tmp).append(" ");
            }
            sb.delete(sb.length() - 1, sb.length());
            sb.append("]");
            return sb.toString();
        }
        catch (Exception e)
        {
            log.error("原始数据转换为16进制错误：{}", e.getMessage(), e);
        }
        return "";
    }

}
