package com.lingniu.ingest.vehiclestate;

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 org.junit.jupiter.api.Test;

import java.util.Optional;

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

class VehicleStateEnvelopeIngestorTest {

    @Test
    void parsesEnvelopeBytesAndUpdatesRepository() {
        InMemoryVehicleStateRepository repository = new InMemoryVehicleStateRepository();
        VehicleStateEnvelopeIngestor ingestor = new VehicleStateEnvelopeIngestor(new VehicleStateUpdater(repository));

        assertThat(ingestor).isInstanceOf(EnvelopeIngestor.class);
        ingestor.ingest(envelope().toByteArray());

        assertThat(repository.getState("VIN001")).hasValueSatisfying(json -> assertThat(json).contains("speed_kmh"));
    }

    @Test
    void rejectsInvalidEnvelopeBytes() {
        VehicleStateEnvelopeIngestor ingestor =
                new VehicleStateEnvelopeIngestor(new VehicleStateUpdater(new EmptyRepository()));

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

    @Test
    void tryIngestInvalidEnvelopeBytesReturnsInvalidWithoutUpdatingRepository() {
        InMemoryVehicleStateRepository repository = new InMemoryVehicleStateRepository();
        VehicleStateEnvelopeIngestor ingestor = new VehicleStateEnvelopeIngestor(new VehicleStateUpdater(repository));

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

        assertThat(result.status()).isEqualTo(EnvelopeIngestResult.Status.INVALID_ENVELOPE);
        assertThat(result.message()).contains("VehicleEnvelope");
        assertThat(repository.getState("VIN001")).isEmpty();
    }

    @Test
    void rawArchiveEnvelopeIsIgnoredWithoutUpdatingRepository() {
        InMemoryVehicleStateRepository repository = new InMemoryVehicleStateRepository();
        VehicleStateEnvelopeIngestor ingestor = new VehicleStateEnvelopeIngestor(new VehicleStateUpdater(repository));
        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");
        assertThat(repository.getState("VIN001")).isEmpty();
        assertThat(repository.getLocation("VIN001")).isEmpty();
        assertThat(repository.getSafety("VIN001")).isEmpty();
        assertThat(repository.getLastEvent("VIN001")).isEmpty();
    }

    private static VehicleEnvelope envelope() {
        return VehicleEnvelope.newBuilder()
                .setEventId("event-1")
                .setVin("VIN001")
                .setSource("GB32960")
                .setEventTimeMs(1)
                .setIngestTimeMs(2)
                .setTelemetrySnapshot(TelemetrySnapshot.newBuilder()
                        .setEventType("REALTIME")
                        .addFields(TelemetryField.newBuilder()
                                .setKey("speed_kmh")
                                .setValueType("DOUBLE")
                                .setValue("80.5")
                                .setUnit("km/h")))
                .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();
    }

    private static final class EmptyRepository implements VehicleStateRepository {
        @Override public void putState(String vin, String json) {}
        @Override public void putLocation(String vin, String json) {}
        @Override public void putSafety(String vin, String json) {}
        @Override public void putLastEvent(String vin, String json) {}
        @Override public Optional<String> getState(String vin) { return Optional.empty(); }
        @Override public Optional<String> getLocation(String vin) { return Optional.empty(); }
        @Override public Optional<String> getSafety(String vin) { return Optional.empty(); }
        @Override public Optional<String> getLastEvent(String vin) { return Optional.empty(); }
    }
}
