package com.lingniu.ingest.protocol.jt808.codec;

import com.lingniu.ingest.api.spi.DecodeException;
import com.lingniu.ingest.api.spi.FrameDecoder;
import com.lingniu.ingest.codec.BccChecksum;
import com.lingniu.ingest.codec.BcdCodec;
import com.lingniu.ingest.protocol.jt808.model.Jt808Body;
import com.lingniu.ingest.protocol.jt808.model.Jt808Header;
import com.lingniu.ingest.protocol.jt808.model.Jt808Message;

import java.nio.ByteBuffer;
import java.util.Arrays;

/**
 * JT808 完整帧（已反转义、含校验码，不含首尾 0x7e） → {@link Jt808Message}。
 *
 * <p>入参 {@link ByteBuffer} 的生命周期由调用方管理，本类只读取。
 */
public final class Jt808MessageDecoder implements FrameDecoder<Jt808Message> {

    private static final int HEADER_LEN_2013 = 12;
    private static final int HEADER_LEN_2019 = 16; // + phone 延长 4 字节

    private final BodyParserRegistry registry;

    public Jt808MessageDecoder(BodyParserRegistry registry) {
        this.registry = registry;
    }

    @Override
    public Jt808Message decode(ByteBuffer buffer) {
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);

        if (bytes.length < HEADER_LEN_2013 + 1) {
            throw new DecodeException("jt808 frame too short: " + bytes.length);
        }

        // 校验码：从 msgId 开始到校验码前一字节
        byte expected = bytes[bytes.length - 1];
        if (!BccChecksum.verify(bytes, 0, bytes.length - 1, expected)) {
            throw new DecodeException("jt808 XOR checksum mismatch");
        }

        int messageId = ((bytes[0] & 0xFF) << 8) | (bytes[1] & 0xFF);
        int attrs = ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);
        int bodyLength = attrs & 0x03FF;
        int encryptType = (attrs >> 10) & 0x07;
        boolean subpacket = (attrs & 0x2000) != 0;
        // JT/T 808-2019 通过属性 bit14 标识，终端手机号从 6B BCD 扩展到 10B BCD。
        boolean v2019 = (attrs & 0x4000) != 0;

        Jt808Header.ProtocolVersion version = v2019
                ? Jt808Header.ProtocolVersion.V2019
                : Jt808Header.ProtocolVersion.V2013;
        int phoneLen = v2019 ? 10 : 6;
        int headerLen = 4 /* msgId+attrs */ + (v2019 ? 1 : 0) /* protocol version */ + phoneLen + 2 /* serial */;
        int phoneOffset = 4 + (v2019 ? 1 : 0);

        String phone = BcdCodec.decode(Arrays.copyOfRange(bytes, phoneOffset, phoneOffset + phoneLen))
                .replaceAll("^0+", "");
        int serial = ((bytes[phoneOffset + phoneLen] & 0xFF) << 8)
                | (bytes[phoneOffset + phoneLen + 1] & 0xFF);

        int totalPkgs = 0, pkgSeq = 0;
        int bodyStart = headerLen;
        if (subpacket) {
            // 分包头紧跟消息流水号之后；当前只暴露包总数/序号，重组由上游或后续模块处理。
            totalPkgs = ((bytes[bodyStart] & 0xFF) << 8) | (bytes[bodyStart + 1] & 0xFF);
            pkgSeq = ((bytes[bodyStart + 2] & 0xFF) << 8) | (bytes[bodyStart + 3] & 0xFF);
            bodyStart += 4;
        }

        int bodyEnd = bodyStart + bodyLength;
        if (bodyEnd > bytes.length - 1) {
            throw new DecodeException("jt808 body length " + bodyLength + " exceeds frame");
        }

        Jt808Header header = new Jt808Header(
                messageId, bodyLength, encryptType, subpacket, version,
                phone, serial, totalPkgs, pkgSeq);

        ByteBuffer bodyBuf = ByteBuffer.wrap(bytes, bodyStart, bodyLength);
        BodyParser parser = registry.find(messageId);
        Jt808Body body;
        if (parser == null) {
            // 未实现的消息体保持 Raw，仍可进入 archive/event-history，避免协议扩展导致数据丢失。
            byte[] raw = new byte[bodyLength];
            bodyBuf.get(raw);
            body = new Jt808Body.Raw(messageId, raw);
        } else {
            body = parser.parse(header, bodyBuf);
        }
        return new Jt808Message(header, body);
    }
}
