package com.lingniu.ingest.protocol.jt1078;

import com.lingniu.ingest.api.ProtocolId;
import com.lingniu.ingest.api.event.RawArchiveKeys;
import com.lingniu.ingest.api.event.VehicleEvent;
import com.lingniu.ingest.identity.InMemoryVehicleIdentityService;
import com.lingniu.ingest.identity.VehicleIdentityBinding;
import com.lingniu.ingest.protocol.jt1078.handler.Jt1078MalformedRtpHandler;
import com.lingniu.ingest.protocol.jt1078.media.Jt1078MalformedRtpPacket;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

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

class Jt1078MalformedRtpHandlerTest {

    @Test
    void malformedRtpComponentsRequireInjectedIdentityResolver() {
        assertThat(Arrays.stream(Jt1078MalformedRtpHandler.class.getConstructors())
                .map(Constructor::getParameterCount))
                .doesNotContain(0);
        assertThat(Arrays.stream(com.lingniu.ingest.protocol.jt1078.media.Jt1078MalformedRtpEventPublisher.class
                .getConstructors()))
                .noneSatisfy(constructor -> assertThat(constructor.getParameterTypes())
                        .containsExactly(Consumer.class));
    }

    @Test
    void malformedRtpRawFramePayloadMapsToPassthroughWithDiagnostics() {
        byte[] raw = new byte[]{0x30, 0x31, 0x63, 0x64};
        Jt1078MalformedRtpHandler handler = new Jt1078MalformedRtpHandler(new InMemoryVehicleIdentityService());

        List<VehicleEvent> events = handler.onMalformedRtp(new Jt1078MalformedRtpPacket(
                "udp",
                "10.0.0.8:1078",
                raw,
                raw.length,
                "jt1078 udp rtp packet too short: 4",
                Instant.parse("2026-06-22T08:30:00Z")));

        assertThat(events).singleElement().satisfies(event -> {
            assertThat(event).isInstanceOf(VehicleEvent.Passthrough.class);
            VehicleEvent.Passthrough passthrough = (VehicleEvent.Passthrough) event;
            assertThat(passthrough.source()).isEqualTo(ProtocolId.JT1078);
            assertThat(passthrough.vin()).isEqualTo("unknown");
            assertThat(passthrough.eventTime()).isEqualTo(Instant.parse("2026-06-22T08:30:00Z"));
            assertThat(passthrough.data()).containsExactly(raw);
            assertThat(passthrough.metadata())
                    .containsEntry("transport", "udp")
                    .containsEntry("peer", "10.0.0.8:1078")
                    .containsEntry("vin", "unknown")
                    .containsEntry("malformedRtp", "true")
                    .containsEntry("parseError", "true")
                    .containsEntry("parseErrorMessage", "jt1078 udp rtp packet too short: 4")
                    .containsEntry("packetLength", "4");
        });
    }

    @Test
    void malformedRtpHandlerResolvesVinBySim() {
        InMemoryVehicleIdentityService identity = new InMemoryVehicleIdentityService();
        identity.bind(new VehicleIdentityBinding(
                ProtocolId.JT1078, "LNVIN107800000001", "013812345678", "", ""));
        Jt1078MalformedRtpHandler handler = new Jt1078MalformedRtpHandler(identity);

        List<VehicleEvent> events = handler.onMalformedRtp(new Jt1078MalformedRtpPacket(
                "tcp",
                "10.0.0.8:1078",
                "013812345678",
                new byte[]{0x30, 0x31, 0x63, 0x64},
                128,
                "jt1078 rtp packet too large: 128",
                Instant.parse("2026-06-22T08:30:00Z")));

        assertThat(events).singleElement().satisfies(event -> {
            assertThat(event.vin()).isEqualTo("LNVIN107800000001");
            assertThat(event.metadata())
                    .containsEntry("vin", "LNVIN107800000001")
                    .containsEntry("sim", "013812345678")
                    .containsEntry("identityResolved", "true")
                    .containsEntry("identitySource", "BOUND_PHONE");
        });
    }

    @Test
    void identityResolverFailureStillProducesPassthroughWithRawPayload() {
        Jt1078MalformedRtpHandler handler = new Jt1078MalformedRtpHandler(lookup -> {
            throw new IllegalStateException("identity backend unavailable");
        });
        byte[] raw = new byte[]{0x30, 0x31, 0x63, 0x64};

        List<VehicleEvent> events = handler.onMalformedRtp(new Jt1078MalformedRtpPacket(
                "udp",
                "10.0.0.8:1078",
                "013812345678",
                raw,
                raw.length,
                "jt1078 udp rtp packet too short: 4",
                Instant.parse("2026-06-22T08:30:00Z")));

        assertThat(events).singleElement().satisfies(event -> {
            assertThat(event).isInstanceOf(VehicleEvent.Passthrough.class);
            VehicleEvent.Passthrough passthrough = (VehicleEvent.Passthrough) event;
            assertThat(passthrough.vin()).isEqualTo("unknown");
            assertThat(passthrough.data()).containsExactly(raw);
            assertThat(passthrough.metadata())
                    .containsEntry("vin", "unknown")
                    .containsEntry("sim", "013812345678")
                    .containsEntry("identityResolved", "false")
                    .containsEntry("identitySource", "UNKNOWN")
                    .containsEntry("identityError", "true")
                    .containsEntry("identityErrorMessage", "identity backend unavailable")
                    .containsEntry("parseError", "true");
        });
    }

    @Test
    void dispatcherEnrichmentCanAttachRawArchiveReferenceToMalformedRtpPassthrough() {
        byte[] raw = new byte[]{0x30, 0x31, 0x63, 0x64};
        Jt1078MalformedRtpHandler handler = new Jt1078MalformedRtpHandler(new InMemoryVehicleIdentityService());

        VehicleEvent.Passthrough event = (VehicleEvent.Passthrough) handler.onMalformedRtp(new Jt1078MalformedRtpPacket(
                "udp", "10.0.0.8:1078", raw, raw.length, "bad", Instant.parse("2026-06-22T08:30:00Z")))
                .getFirst();

        assertThat(event.metadata()).doesNotContainKey(RawArchiveKeys.META_URI);
        assertThat(event.data()).containsExactly(raw);
    }
}
