package com.lingniu.ingest.protocol.gb32960.codec.parser.vendor.guangdong;

import com.lingniu.ingest.protocol.gb32960.codec.Gb32960BodyParser;
import com.lingniu.ingest.protocol.gb32960.codec.Gb32960MessageDecoder;
import com.lingniu.ingest.protocol.gb32960.codec.InfoBlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.InfoBlockParserRegistry;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.VehicleV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.model.InfoBlock;
import com.lingniu.ingest.protocol.gb32960.model.ProtocolVersion;
import org.junit.jupiter.api.Test;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * 验证 {@link GdFcVendorTlvBlockParser} 严格按 length 消费字节，不会贪心吞光后续信息体。
 *
 * <p>这是关键回归 case：用户问"加 0x83 兜底会不会把后面其它块也吃掉？"——通过构造
 * 一个 {@code 0x83 TLV + 0x01 Vehicle} 串联的 body，断言 BodyParser 解出**两个块**而非一个。
 */
class GdFcVendorTlvBlockParserTest {

    @Test
    void parsesPayloadOfDeclaredLengthExactly() {
        // 0x83 typeCode 已被外层消费，buffer 从 length 字段开始
        byte[] data = bytes(
                0x00, 0x05,                     // length = 5
                0xAA, 0xBB, 0xCC, 0xDD, 0xEE);  // 5 bytes payload
        ByteBuffer buf = ByteBuffer.wrap(data);

        InfoBlock.GuangdongFc.VendorTlv tlv =
                (InfoBlock.GuangdongFc.VendorTlv) new GdFcVendorTlvBlockParser(0x83).parse(buf);

        assertThat(tlv.typeCode()).isEqualTo(0x83);
        assertThat(tlv.declaredLength()).isEqualTo(5);
        assertThat(tlv.payload()).containsExactly(0xAA, 0xBB, 0xCC, 0xDD, 0xEE);
        assertThat(buf.remaining()).as("应正好消费 2+5=7 字节").isZero();
    }

    @Test
    void doesNotSwallowSubsequentBlocksWhenChainedInBodyParser() {
        // 构造 body：0x83 TLV(length=4) + 0x01 Vehicle(20B 标准布局)
        ByteArrayOutputStream body = new ByteArrayOutputStream();
        // 0x83 TLV：3+4 = 7 字节
        body.write(0x83);
        body.write(0x00); body.write(0x04);     // length = 4
        body.write(0x11); body.write(0x22); body.write(0x33); body.write(0x44);
        // 0x01 Vehicle: typeCode + 20 字节 body
        body.write(0x01);
        body.write(0x01);                       // vehicleState
        body.write(0x03);                       // chargingState
        body.write(0x02);                       // runningMode
        write16(body, 0);                       // speed
        write32(body, 100_000L);                // mileage
        write16(body, 5605);                    // totalVoltage
        write16(body, 10000);                   // totalCurrent
        body.write(85);                         // SOC
        body.write(0x01);                       // dcDcStatus
        body.write(0x00);                       // gear
        write16(body, 10000);                   // insulation
        body.write(0x00);                       // accelPedal
        body.write(0x00);                       // brakePedal

        // 装配 body parser：注册 0x83 vendor TLV + 0x01 标准 Vehicle
        InfoBlockParserRegistry registry = new InfoBlockParserRegistry(List.of(
                new GdFcVendorTlvBlockParser(0x83),
                new VehicleV2016BlockParser()));
        Gb32960BodyParser parser = new Gb32960BodyParser(registry);

        ByteBuffer buf = ByteBuffer.wrap(body.toByteArray());
        Gb32960MessageDecoder.BodyParseResult result = parser.parse(ProtocolVersion.V2016, buf);

        // 关键断言：两个块都解析出来了，0x01 没被 0x83 吞掉
        assertThat(result.blocks()).hasSize(2);
        assertThat(result.blocks().get(0)).isInstanceOf(InfoBlock.GuangdongFc.VendorTlv.class);
        assertThat(result.blocks().get(1)).isInstanceOf(InfoBlock.Gb32960V2016.Vehicle.class);

        InfoBlock.GuangdongFc.VendorTlv tlv = (InfoBlock.GuangdongFc.VendorTlv) result.blocks().get(0);
        assertThat(tlv.declaredLength()).isEqualTo(4);
        assertThat(tlv.payload()).containsExactly(0x11, 0x22, 0x33, 0x44);

        InfoBlock.Gb32960V2016.Vehicle v = (InfoBlock.Gb32960V2016.Vehicle) result.blocks().get(1);
        assertThat(v.socPercent()).isEqualTo(85);
        assertThat(v.totalVoltageV()).isEqualTo(560.5);
    }

    @Test
    void truncatesPayloadIfDeclaredLengthExceedsRemaining() {
        // 防御场景：length 字段声称 100 字节但 buffer 只剩 3 字节
        byte[] data = bytes(0x00, 0x64, 0x01, 0x02, 0x03);
        ByteBuffer buf = ByteBuffer.wrap(data);
        InfoBlock.GuangdongFc.VendorTlv tlv =
                (InfoBlock.GuangdongFc.VendorTlv) new GdFcVendorTlvBlockParser(0x83).parse(buf);
        assertThat(tlv.declaredLength()).isEqualTo(100);
        assertThat(tlv.payload()).hasSize(3).containsExactly(0x01, 0x02, 0x03);
    }

    private static byte[] bytes(int... values) {
        byte[] out = new byte[values.length];
        for (int i = 0; i < values.length; i++) out[i] = (byte) values[i];
        return out;
    }

    private static void write16(ByteArrayOutputStream os, int v) {
        os.write((v >> 8) & 0xFF);
        os.write(v & 0xFF);
    }

    private static void write32(ByteArrayOutputStream os, long v) {
        os.write((int) ((v >> 24) & 0xFF));
        os.write((int) ((v >> 16) & 0xFF));
        os.write((int) ((v >> 8) & 0xFF));
        os.write((int) (v & 0xFF));
    }
}
