package com.lingniu.ingest.tdenginehistory;

import com.lingniu.ingest.sink.kafka.proto.LocationPayload;
import com.lingniu.ingest.sink.kafka.proto.ParseStatusProto;
import com.lingniu.ingest.sink.kafka.proto.RawArchiveRef;
import com.lingniu.ingest.sink.kafka.proto.RawFrameFactPayload;
import com.lingniu.ingest.sink.kafka.proto.VehicleEnvelope;
import org.junit.jupiter.api.Test;

import java.time.Instant;

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

class TdengineEnvelopeRowsTest {

    @Test
    void mapsRawFrameFactEnvelopeToRawFrameRow() {
        VehicleEnvelope envelope = VehicleEnvelope.newBuilder()
                .setEventId("evt-raw-1")
                .setVin("VIN123")
                .setSource("JT808")
                .setEventTimeMs(1_772_000_001_234L)
                .setIngestTimeMs(1_772_000_001_999L)
                .putMetadata("channel", "tcp-808")
                .setRawFrameFact(RawFrameFactPayload.newBuilder()
                        .setFrameId("frame-1")
                        .setVehicleKey("jt808:g7gps")
                        .setPhone("013800000000")
                        .setMessageId(0x0200)
                        .setSubType(0)
                        .setRawUri("archive://jt808/2026/06/29/frame-1.bin")
                        .setChecksum("sha256:abc")
                        .setRawSizeBytes(67)
                        .setParseStatus(ParseStatusProto.PARSE_STATUS_SUCCEEDED)
                        .setPeer("10.0.0.8:30001")
                        .putMetadata("auth", "passed"))
                .build();

        TdengineRawFrameRow row = TdengineEnvelopeRows.rawFrame(envelope).orElseThrow();

        assertThat(row.ts()).isEqualTo(Instant.ofEpochMilli(1_772_000_001_234L));
        assertThat(row.receivedAt()).isEqualTo(Instant.ofEpochMilli(1_772_000_001_999L));
        assertThat(row.protocol()).isEqualTo("JT808");
        assertThat(row.vehicleKey()).isEqualTo("jt808:g7gps");
        assertThat(row.vin()).isEqualTo("VIN123");
        assertThat(row.phone()).isEqualTo("013800000000");
        assertThat(row.messageId()).isEqualTo(0x0200);
        assertThat(row.rawUri()).isEqualTo("archive://jt808/2026/06/29/frame-1.bin");
        assertThat(row.metadataJson()).contains("\"channel\":\"tcp-808\"", "\"auth\":\"passed\"");
    }

    @Test
    void expandsRawFrameTsWithRawArchiveEventIdSequenceToAvoidSameMillisecondOverwrite() {
        VehicleEnvelope first = rawFrameEnvelope("1782729152494000");
        VehicleEnvelope second = rawFrameEnvelope("1782729152494001");

        TdengineRawFrameRow firstRow = TdengineEnvelopeRows.rawFrame(first).orElseThrow();
        TdengineRawFrameRow secondRow = TdengineEnvelopeRows.rawFrame(second).orElseThrow();

        assertThat(firstRow.ts()).isEqualTo(Instant.ofEpochMilli(1_782_729_152_494L));
        assertThat(secondRow.ts()).isEqualTo(Instant.ofEpochMilli(1_782_729_152_495L));
        assertThat(firstRow.eventTime()).isEqualTo(Instant.ofEpochMilli(1_782_729_152_494L));
        assertThat(secondRow.eventTime()).isEqualTo(Instant.ofEpochMilli(1_782_729_152_494L));
    }

    @Test
    void mapsLocationEnvelopeToVehicleLocationRow() {
        VehicleEnvelope envelope = VehicleEnvelope.newBuilder()
                .setEventId("evt-location-1")
                .setVin("VIN32960")
                .setSource("GB32960")
                .setEventTimeMs(1_772_000_002_000L)
                .setIngestTimeMs(1_772_000_002_321L)
                .putMetadata("vehicle_key", "vin:VIN32960")
                .putMetadata("frame_id", "frame-location-1")
                .setRawArchive(RawArchiveRef.newBuilder()
                        .setUri("archive://gb32960/2026/06/29/frame-location-1.bin")
                        .setChecksum("sha256:def")
                        .setSizeBytes(98))
                .setLocation(LocationPayload.newBuilder()
                        .setLongitude(113.12)
                        .setLatitude(23.45)
                        .setAltitudeM(8.0)
                        .setSpeedKmh(42.5)
                        .setDirectionDeg(91.0)
                        .setAlarmFlag(1)
                        .setStatusFlag(3))
                .build();

        TdengineLocationRow row = TdengineEnvelopeRows.location(envelope).orElseThrow();

        assertThat(row.ts()).isEqualTo(Instant.ofEpochMilli(1_772_000_002_000L));
        assertThat(row.factId()).isEqualTo("evt-location-1");
        assertThat(row.frameId()).isEqualTo("frame-location-1");
        assertThat(row.protocol()).isEqualTo("GB32960");
        assertThat(row.vehicleKey()).isEqualTo("vin:VIN32960");
        assertThat(row.longitude()).isEqualTo(113.12);
        assertThat(row.latitude()).isEqualTo(23.45);
        assertThat(row.speedKmh()).isEqualTo(42.5);
        assertThat(row.totalMileageKm()).isNull();
        assertThat(row.rawUri()).isEqualTo("archive://gb32960/2026/06/29/frame-location-1.bin");
    }

    @Test
    void mapsLocationRawUriFromMetadataWhenRawArchiveRefIsAbsent() {
        VehicleEnvelope envelope = VehicleEnvelope.newBuilder()
                .setEventId("evt-location-metadata-raw")
                .setVin("unknown")
                .setSource("JT808")
                .setEventTimeMs(1_772_000_002_000L)
                .setIngestTimeMs(1_772_000_002_321L)
                .putMetadata("phone", "13079963291")
                .putMetadata("rawArchiveUri", "archive://jt808/metadata-only.bin")
                .setLocation(LocationPayload.newBuilder()
                        .setLongitude(119.12)
                        .setLatitude(29.45))
                .build();

        TdengineLocationRow row = TdengineEnvelopeRows.location(envelope).orElseThrow();

        assertThat(row.rawUri()).isEqualTo("archive://jt808/metadata-only.bin");
    }

    @Test
    void mapsJt808UnknownVinRowsToPhoneVehicleKey() {
        VehicleEnvelope envelope = VehicleEnvelope.newBuilder()
                .setEventId("evt-jt808-phone-1")
                .setVin("unknown")
                .setSource("JT808")
                .setEventTimeMs(1_772_000_004_000L)
                .setIngestTimeMs(1_772_000_004_500L)
                .putMetadata("phone", "13079963291")
                .putMetadata("frame_id", "frame-jt808-phone-1")
                .setRawFrameFact(RawFrameFactPayload.newBuilder()
                        .setFrameId("frame-jt808-phone-1")
                        .setPhone("13079963291")
                        .setMessageId(0x0200)
                        .setRawUri("archive://jt808/frame-jt808-phone-1.bin")
                        .setChecksum("sha256:phone")
                        .setRawSizeBytes(88)
                        .setParseStatus(ParseStatusProto.PARSE_STATUS_SUCCEEDED))
                .setLocation(LocationPayload.newBuilder()
                        .setLongitude(119.1)
                        .setLatitude(29.2))
                .build();

        assertThat(TdengineEnvelopeRows.rawFrame(envelope).orElseThrow().vehicleKey())
                .isEqualTo("jt808:13079963291");
        assertThat(TdengineEnvelopeRows.location(envelope).orElseThrow().vehicleKey())
                .isEqualTo("jt808:13079963291");
    }

    private static VehicleEnvelope rawFrameEnvelope(String rawArchiveEventId) {
        return VehicleEnvelope.newBuilder()
                .setEventId(rawArchiveEventId)
                .setVin("unknown")
                .setSource("JT808")
                .setEventTimeMs(1_782_729_152_494L)
                .setIngestTimeMs(1_782_729_152_539L)
                .putMetadata("phone", "13079973200")
                .putMetadata("rawArchiveEventId", rawArchiveEventId)
                .setRawFrameFact(RawFrameFactPayload.newBuilder()
                        .setFrameId("rf-" + rawArchiveEventId)
                        .setPhone("13079973200")
                        .setMessageId(0x0200)
                        .setRawUri("archive://2026/06/29/JT808/unknown/" + rawArchiveEventId + ".bin")
                        .setChecksum("sha256:" + rawArchiveEventId)
                        .setRawSizeBytes(41)
                        .setParseStatus(ParseStatusProto.PARSE_STATUS_NOT_PARSED)
                        .putMetadata("rawArchiveEventId", rawArchiveEventId))
                .build();
    }
}
