package com.lingniu.ingest.vehiclestat;

import com.lingniu.ingest.api.consumer.EnvelopeIngestResult;
import com.lingniu.ingest.api.consumer.EnvelopeIngestor;
import com.lingniu.ingest.sink.kafka.proto.RawArchiveRef;
import com.lingniu.ingest.sink.kafka.proto.TelemetryField;
import com.lingniu.ingest.sink.kafka.proto.TelemetrySnapshot;
import com.lingniu.ingest.sink.kafka.proto.VehicleEnvelope;
import com.lingniu.ingest.vehiclestat.jt808.Jt808MileageStreamProcessor;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;

class VehicleStatEnvelopeIngestorTest {

    @Test
    void ignoresNonJt808Telemetry() {
        Jt808MileageStreamProcessor jt808Processor = mock(Jt808MileageStreamProcessor.class);
        VehicleEnvelope envelope = envelope("GB32960");
        VehicleStatEnvelopeIngestor ingestor = new VehicleStatEnvelopeIngestor(jt808Processor);

        ingestor.ingest(envelope.toByteArray());
        var result = ingestor.tryIngest(envelope.toByteArray());

        verifyNoInteractions(jt808Processor);
        assertThat(result.status()).isEqualTo(EnvelopeIngestResult.Status.SKIPPED);
        assertThat(result.message()).contains("only accepts JT808");
    }

    @Test
    void routesJt808TelemetryOnlyToJt808MileageProcessorWhenAvailable() {
        Jt808MileageStreamProcessor jt808Processor = mock(Jt808MileageStreamProcessor.class);
        VehicleEnvelope envelope = envelope("JT808");
        VehicleStatEnvelopeIngestor ingestor = new VehicleStatEnvelopeIngestor(jt808Processor);

        assertThat(ingestor).isInstanceOf(EnvelopeIngestor.class);
        ingestor.ingest(envelope.toByteArray());
        var result = ingestor.tryIngest(envelope.toByteArray());

        verify(jt808Processor, times(2)).process(envelope);
        assertThat(result.status()).isEqualTo(EnvelopeIngestResult.Status.PROCESSED);
    }

    @Test
    void rejectsInvalidEnvelopeBytes() {
        VehicleStatEnvelopeIngestor ingestor = new VehicleStatEnvelopeIngestor(mock(Jt808MileageStreamProcessor.class));

        assertThatThrownBy(() -> ingestor.ingest(new byte[]{0x01, 0x02}))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("VehicleEnvelope");
    }

    @Test
    void tryIngestInvalidEnvelopeBytesReturnsInvalidWithoutProcessing() {
        Jt808MileageStreamProcessor jt808Processor = mock(Jt808MileageStreamProcessor.class);
        VehicleStatEnvelopeIngestor ingestor = new VehicleStatEnvelopeIngestor(jt808Processor);

        var result = ingestor.tryIngest(new byte[]{0x01, 0x02});

        assertThat(result.status()).isEqualTo(EnvelopeIngestResult.Status.INVALID_ENVELOPE);
        assertThat(result.message()).contains("VehicleEnvelope");
        verifyNoInteractions(jt808Processor);
    }

    @Test
    void tryIngestEnvelopeWithoutTelemetrySnapshotReturnsSkipped() {
        Jt808MileageStreamProcessor jt808Processor = mock(Jt808MileageStreamProcessor.class);
        VehicleStatEnvelopeIngestor ingestor = new VehicleStatEnvelopeIngestor(jt808Processor);
        VehicleEnvelope envelope = VehicleEnvelope.newBuilder()
                .setEventId("event-login-1")
                .setVin("VIN001")
                .setSource("GB32960")
                .setEventTimeMs(1_782_112_400_000L)
                .setIngestTimeMs(1_782_112_401_000L)
                .build();

        var result = ingestor.tryIngest(envelope.toByteArray());

        assertThat(result.status()).isEqualTo(EnvelopeIngestResult.Status.SKIPPED);
        assertThat(result.eventId()).isEqualTo("event-login-1");
        assertThat(result.message()).contains("telemetry_snapshot");
        verifyNoInteractions(jt808Processor);
    }

    @Test
    void rawArchiveEnvelopeIsIgnoredWithoutProcessing() {
        Jt808MileageStreamProcessor jt808Processor = mock(Jt808MileageStreamProcessor.class);
        VehicleStatEnvelopeIngestor ingestor = new VehicleStatEnvelopeIngestor(jt808Processor);
        VehicleEnvelope envelope = rawArchiveEnvelope();

        ingestor.ingest(envelope.toByteArray());
        var result = ingestor.tryIngest(envelope.toByteArray());

        assertThat(result.status()).isEqualTo(EnvelopeIngestResult.Status.SKIPPED);
        assertThat(result.eventId()).isEqualTo("raw-event-1");
        assertThat(result.vin()).isEqualTo("VIN001");
        assertThat(result.message()).contains("telemetry_snapshot");
        verifyNoInteractions(jt808Processor);
    }

    private static VehicleEnvelope envelope(String source) {
        return VehicleEnvelope.newBuilder()
                .setEventId("event-1")
                .setVin("VIN001")
                .setSource(source)
                .setEventTimeMs(1_782_112_400_000L)
                .setIngestTimeMs(1_782_112_401_000L)
                .setTelemetrySnapshot(TelemetrySnapshot.newBuilder()
                        .setEventType("REALTIME")
                        .addFields(TelemetryField.newBuilder()
                                .setKey("total_mileage_km")
                                .setValueType("DOUBLE")
                                .setValue("123.45")
                                .setQuality("GOOD")))
                .build();
    }

    private static VehicleEnvelope rawArchiveEnvelope() {
        return VehicleEnvelope.newBuilder()
                .setEventId("raw-event-1")
                .setVin("VIN001")
                .setSource("GB32960")
                .setEventTimeMs(1_782_112_400_000L)
                .setIngestTimeMs(1_782_112_401_000L)
                .setRawArchive(RawArchiveRef.newBuilder()
                        .setUri("archive://2026/06/23/GB32960/VIN001/raw-event-1.bin")
                        .setSizeBytes(128)
                        .build())
                .build();
    }
}
