package com.lingniu.ingest.api.event;

import com.lingniu.ingest.api.ProtocolId;
import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.util.Map;

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

class VehicleEventTelemetrySnapshotMapperTest {

    @Test
    void mapsRealtimeEventToInternalTelemetryFields() {
        VehicleEvent.Realtime event = new VehicleEvent.Realtime(
                "event-1",
                "VIN001",
                ProtocolId.GB32960,
                Instant.parse("2026-06-22T08:00:00Z"),
                Instant.parse("2026-06-22T08:00:01Z"),
                "trace-1",
                Map.of("protocolVersion", "V2016", "rawArchiveUri", "archive://raw/event-1.bin"),
                new RealtimePayload(
                        80.5,
                        12345.6,
                        55.0,
                        610.0,
                        -20.5,
                        220.0,
                        100.0,
                        65.0,
                        8.2,
                        35.0,
                        1.1,
                        RealtimePayload.VehicleState.STARTED,
                        RealtimePayload.ChargingState.UNCHARGED,
                        RealtimePayload.RunningMode.FUEL,
                        3,
                        20.0,
                        0.0,
                        113.12,
                        23.45,
                        10.0,
                        180.0,
                        30.0,
                        70.0
                )
        );

        TelemetrySnapshot snapshot = VehicleEventTelemetrySnapshotMapper.toSnapshot(event).orElseThrow();

        assertThat(snapshot.eventId()).isEqualTo("event-1");
        assertThat(snapshot.rawArchiveUri()).isEqualTo("archive://raw/event-1.bin");
        assertThat(snapshot.field("speed_kmh").orElseThrow().value()).isEqualTo("80.5");
        assertThat(snapshot.field("total_mileage_km").orElseThrow().value()).isEqualTo("12345.6");
        assertThat(snapshot.field("vehicle_state").orElseThrow().value()).isEqualTo("STARTED");
        assertThat(snapshot.field("hydrogen_high_pressure_mpa").orElseThrow().value()).isEqualTo("35.0");
        assertThat(snapshot.field("longitude").orElseThrow().value()).isEqualTo("113.12");
    }

    @Test
    void mapsHydrogenLeakAlarmToInternalSafetyFields() {
        VehicleEvent.Alarm event = new VehicleEvent.Alarm(
                "event-2",
                "VIN001",
                ProtocolId.GB32960,
                Instant.parse("2026-06-22T08:00:00Z"),
                Instant.parse("2026-06-22T08:00:01Z"),
                "trace-2",
                Map.of("protocolVersion", "V2016"),
                new AlarmPayload(
                        AlarmPayload.AlarmLevel.CRITICAL,
                        1,
                        "GB32960_ALARM",
                        java.util.List.of("OTH-1"),
                        java.util.Set.of("HYDROGEN_LEAK"),
                        113.12,
                        23.45,
                        AlarmPayload.SafetyCategory.HYDROGEN_LEAK,
                        true,
                        AlarmPayload.HydrogenLeakLevel.CRITICAL,
                        true
                )
        );

        TelemetrySnapshot snapshot = VehicleEventTelemetrySnapshotMapper.toSnapshot(event).orElseThrow();

        assertThat(snapshot.eventType()).isEqualTo("ALARM");
        assertThat(snapshot.field("hydrogen_leak_detected").orElseThrow().value()).isEqualTo("true");
        assertThat(snapshot.field("hydrogen_leak_level").orElseThrow().value()).isEqualTo("CRITICAL");
        assertThat(snapshot.field("hydrogen_leak_action_required").orElseThrow().value()).isEqualTo("true");
        assertThat(snapshot.field("safety_category").orElseThrow().value()).isEqualTo("HYDROGEN_LEAK");
    }

    @Test
    void mapsLocationMileageToInternalTelemetryField() {
        VehicleEvent.Location event = new VehicleEvent.Location(
                "event-location-1",
                "VIN001",
                ProtocolId.JT808,
                Instant.parse("2026-06-22T08:00:00Z"),
                Instant.parse("2026-06-22T08:00:01Z"),
                "trace-location-1",
                Map.of("messageId", "0x200"),
                new LocationPayload(113.12, 23.45, 10.0, 60.0, 90.0, 0, 1, 1234.5)
        );

        TelemetrySnapshot snapshot = VehicleEventTelemetrySnapshotMapper.toSnapshot(event).orElseThrow();

        assertThat(snapshot.field("total_mileage_km").orElseThrow().value()).isEqualTo("1234.5");
        assertThat(snapshot.field("total_mileage_km").orElseThrow().sourcePath())
                .isEqualTo("event.location.totalMileageKm");
    }

    @Test
    void mediaMetaUsesArchiveRefAsQueryableRawArchiveUriFallback() {
        VehicleEvent.MediaMeta event = new VehicleEvent.MediaMeta(
                "media-1",
                "VIN001",
                ProtocolId.JT1078,
                Instant.parse("2026-06-22T08:00:00Z"),
                Instant.parse("2026-06-22T08:00:01Z"),
                "trace-4",
                Map.of("channel", "1"),
                "media-001",
                "jt1078-video-h264-channel-1",
                1024,
                "file:///archive/jt1078/2026/06/22/VIN001/media-001.rtp"
        );

        TelemetrySnapshot snapshot = VehicleEventTelemetrySnapshotMapper.toSnapshot(event).orElseThrow();

        assertThat(snapshot.eventType()).isEqualTo("MEDIA_META");
        assertThat(snapshot.rawArchiveUri())
                .isEqualTo("file:///archive/jt1078/2026/06/22/VIN001/media-001.rtp");
        assertThat(snapshot.field("media_archive_ref").orElseThrow().value())
                .isEqualTo("file:///archive/jt1078/2026/06/22/VIN001/media-001.rtp");
    }

    @Test
    void derivesRawArchiveUriFromRawArchiveKeyMetadata() {
        VehicleEvent.Heartbeat event = new VehicleEvent.Heartbeat(
                "heartbeat-1",
                "VIN001",
                ProtocolId.JT808,
                Instant.parse("2026-06-22T08:00:00Z"),
                Instant.parse("2026-06-22T08:00:01Z"),
                "trace-5",
                Map.of("rawArchiveKey", "2026/06/22/JT808/VIN001/raw-1.bin")
        );

        TelemetrySnapshot snapshot = VehicleEventTelemetrySnapshotMapper.toSnapshot(event).orElseThrow();

        assertThat(snapshot.rawArchiveUri())
                .isEqualTo("archive://2026/06/22/JT808/VIN001/raw-1.bin");
    }

    @Test
    void skipsRawArchiveBecauseItIsStoredByArchiveSink() {
        VehicleEvent.RawArchive raw = new VehicleEvent.RawArchive(
                "raw-1",
                "VIN001",
                ProtocolId.GB32960,
                Instant.parse("2026-06-22T08:00:00Z"),
                Instant.parse("2026-06-22T08:00:01Z"),
                "trace-3",
                Map.of(),
                0x02,
                0,
                new byte[]{0x23, 0x23}
        );

        assertThat(VehicleEventTelemetrySnapshotMapper.toSnapshot(raw)).isEmpty();
    }
}
