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

import com.lingniu.ingest.protocol.jt808.codec.parser.HeartbeatBodyParser;
import com.lingniu.ingest.protocol.jt808.codec.parser.LocationBodyParser;
import com.lingniu.ingest.protocol.jt808.codec.parser.RegisterBodyParser;
import com.lingniu.ingest.protocol.jt808.downlink.Jt808Commands;
import com.lingniu.ingest.protocol.jt808.model.Jt808Body;
import com.lingniu.ingest.protocol.jt808.model.Jt808Message;
import com.lingniu.ingest.protocol.jt808.model.Jt808MessageId;
import org.junit.jupiter.api.Test;

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

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

/**
 * 端到端验证：encoder 生成的下行帧可以被 decoder 反向解析，且字段对称。
 */
class Jt808FrameEncoderTest {

    private final Jt808MessageDecoder decoder = new Jt808MessageDecoder(
            new BodyParserRegistry(List.of(
                    new RegisterBodyParser(),
                    new LocationBodyParser(),
                    new HeartbeatBodyParser())));

    @Test
    void encodeThenDecodePlatformAck() {
        var cmd = Jt808Commands.platformAck(0x1234, 0x0200, 0);
        byte[] framed = Jt808FrameEncoder.encode(cmd.messageId(), "123456789012", 7, cmd.body());

        // strip outer 0x7e and unescape
        byte[] unescaped = Jt808Escape.unescape(framed, 1, framed.length - 2);
        Jt808Message msg = decoder.decode(ByteBuffer.wrap(unescaped));

        assertThat(msg.header().messageId()).isEqualTo(Jt808MessageId.PLATFORM_GENERAL_RESPONSE);
        assertThat(msg.header().phone()).isEqualTo("123456789012");
        assertThat(msg.header().serialNo()).isEqualTo(7);
        assertThat(msg.body()).isInstanceOf(Jt808Body.Raw.class);
        Jt808Body.Raw raw = (Jt808Body.Raw) msg.body();
        // body: ackSerial(2) + ackMsgId(2) + result(1) = 5 bytes
        assertThat(raw.bytes()).hasSize(5);
    }

    @Test
    void encodeQueryLocationProducesFramedWith7eBoundary() {
        byte[] framed = Jt808FrameEncoder.encode(
                Jt808MessageId.PLATFORM_QUERY_LOCATION, "123456789012", 1, new byte[0]);
        assertThat(framed[0]).isEqualTo((byte) 0x7e);
        assertThat(framed[framed.length - 1]).isEqualTo((byte) 0x7e);
        // 中间不应出现裸 0x7e
        for (int i = 1; i < framed.length - 1; i++) {
            assertThat(framed[i]).isNotEqualTo((byte) 0x7e);
        }
    }

    @Test
    void rejectsOversizedBodyWhenSubpackageIsNotUsed() {
        assertThatThrownBy(() -> Jt808FrameEncoder.encode(
                Jt808MessageId.PLATFORM_SET_PARAMS, "123456789012", 1, new byte[1024]))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("jt808 body too large");
    }

    @Test
    void encodesOversizedBodyAsSubpackages() {
        byte[] body = new byte[2048];
        for (int i = 0; i < body.length; i++) {
            body[i] = (byte) i;
        }

        List<byte[]> frames = Jt808FrameEncoder.encodeSubpackages(
                Jt808MessageId.PLATFORM_SET_PARAMS, "123456789012", 9, body);

        assertThat(frames).hasSize(3);
        java.io.ByteArrayOutputStream joined = new java.io.ByteArrayOutputStream();
        for (int i = 0; i < frames.size(); i++) {
            byte[] unescaped = Jt808Escape.unescape(frames.get(i), 1, frames.get(i).length - 2);
            Jt808Message msg = decoder.decode(ByteBuffer.wrap(unescaped));
            assertThat(msg.header().subpacket()).isTrue();
            assertThat(msg.header().totalPackets()).isEqualTo(3);
            assertThat(msg.header().packetSeq()).isEqualTo(i + 1);
            assertThat(msg.header().serialNo()).isEqualTo(9);
            assertThat(msg.body()).isInstanceOf(Jt808Body.Raw.class);
            joined.writeBytes(((Jt808Body.Raw) msg.body()).bytes());
        }
        assertThat(joined.toByteArray()).containsExactly(body);
    }
}
