package at.procon.eventhub.tachograph.service;

import at.procon.eventhub.dto.AcquisitionStrategy;
import at.procon.eventhub.dto.EventFamily;
import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.dto.EventSourceDto;
import at.procon.eventhub.importing.ImportPlanItemDto;
import at.procon.eventhub.importing.ImportTimeChunkDto;
import at.procon.eventhub.importing.extraction.ExtractionContext;
import at.procon.eventhub.processing.service.UnifiedEventTimelineReconstructor;
import at.procon.eventhub.service.EventDetailsFactory;
import at.procon.eventhub.tachograph.dto.TachographImportRequest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class TachographDbRowMapperTimelineMetadataTest {

    private final EventDetailsFactory detailsFactory = new EventDetailsFactory(new ObjectMapper());
    private final UnifiedEventTimelineReconstructor reconstructor = new UnifiedEventTimelineReconstructor();

    @Test
    void activityRowsExposeTimelineMetadataForReconstruction() throws SQLException {
        CardActivityRowMapper mapper = new CardActivityRowMapper(detailsFactory);
        ExtractionContext<TachographImportRequest> context = context(EventFamily.DRIVER_ACTIVITY, "DRIVER_CARD", "CARD_ACTIVITY");
        EventHubEventDto started = mapper.map(activityRow("START", "2026-05-22T06:00:00Z"), 0, context);
        EventHubEventDto ended = mapper.map(activityRow("END", "2026-05-22T07:15:00Z"), 1, context);

        JsonNode raw = started.payload().get("raw");
        assertThat(raw.get("intervalId").asText()).isEqualTo("TACHOGRAPH:CARD_ACTIVITY:CA-1");
        assertThat(raw.get("sourceKind").asText()).isEqualTo("DRIVER_CARD");
        assertThat(raw.get("extractionCode").asText()).isEqualTo("CARD_ACTIVITY");
        assertThat(raw.get("sourceRowIds").get(0).asText()).isEqualTo("CA-1");
        assertThat(raw.get("driverKey").asText()).contains("driver-1");
        assertThat(raw.get("vehicleKey").asText()).contains("VIN123");
        assertThat(raw.get("registrationKey").asText()).contains("W-12345");

        var timeline = reconstructor.reconstruct(UUID.randomUUID(), "driver-1", List.of(started, ended));

        assertThat(timeline.activityIntervals()).hasSize(1);
        var interval = timeline.activityIntervals().getFirst();
        assertThat(interval.intervalId()).isEqualTo("TACHOGRAPH:CARD_ACTIVITY:CA-1");
        assertThat(interval.activityType()).isEqualTo("DRIVE");
        assertThat(interval.sourceKind()).isEqualTo("DRIVER_CARD");
        assertThat(interval.sourceIntervalIds()).containsExactly("CA-1");
        assertThat(interval.registrationKey()).contains("W-12345");
        assertThat(interval.vehicleKey()).contains("VIN123");
        assertThat(interval.durationSeconds()).isEqualTo(4_500L);
    }

    @Test
    void cardUsageRowsExposeTimelineMetadataForVehicleUsageReconstruction() throws SQLException {
        CardVehiclesUsedCardEventRowMapper mapper = new CardVehiclesUsedCardEventRowMapper(detailsFactory);
        ExtractionContext<TachographImportRequest> context = context(EventFamily.DRIVER_CARD, "DRIVER_CARD", "CARD_VEHICLES_USED");
        EventHubEventDto inserted = mapper.map(cardUsageRow("CARD_INSERTED", "INSERT", "2026-05-22T06:00:00Z", 120_000L), 0, context);
        EventHubEventDto withdrawn = mapper.map(cardUsageRow("CARD_WITHDRAWN", "WITHDRAW", "2026-05-22T18:00:00Z", 120_550L), 1, context);

        JsonNode raw = inserted.payload().get("raw");
        assertThat(raw.get("intervalId").asText()).isEqualTo("TACHOGRAPH:CARD_VEHICLES_USED:CVU-1");
        assertThat(raw.get("sourceRowIds").get(0).asText()).isEqualTo("CVU-1");
        assertThat(raw.get("registrationKey").asText()).contains("W-12345");

        var timeline = reconstructor.reconstruct(UUID.randomUUID(), "driver-1", List.of(inserted, withdrawn));

        assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
        var usage = timeline.vehicleUsageIntervals().getFirst();
        assertThat(usage.intervalId()).isEqualTo("TACHOGRAPH:CARD_VEHICLES_USED:CVU-1");
        assertThat(usage.sourceKind()).isEqualTo("DRIVER_CARD");
        assertThat(usage.sourceIntervalIds()).containsExactly("CVU-1");
        assertThat(usage.registrationKey()).contains("W-12345");
        assertThat(usage.vehicleKey()).contains("VIN123");
        assertThat(usage.odometerBeginKm()).isEqualTo(120L);
        assertThat(usage.odometerEndKm()).isEqualTo(120L);
        assertThat(usage.durationSeconds()).isEqualTo(43_200L);
    }

    private ResultSet activityRow(String lifecycle, String occurredAt) throws SQLException {
        ResultSet rs = commonRow("CA-1", occurredAt, 120_000L);
        when(rs.getString("external_source_event_id")).thenReturn("CARD_ACTIVITY:CA-1:" + lifecycle);
        when(rs.getString("event_type")).thenReturn("DRIVE");
        when(rs.getString("lifecycle")).thenReturn(lifecycle);
        when(rs.getObject("activity_code")).thenReturn(3);
        when(rs.getObject("card_slot")).thenReturn(0);
        when(rs.getObject("card_status")).thenReturn(0);
        when(rs.getObject("driving_status")).thenReturn(0);
        return rs;
    }

    private ResultSet cardUsageRow(String eventType, String lifecycle, String occurredAt, Long odometerM) throws SQLException {
        ResultSet rs = commonRow("CVU-1", occurredAt, odometerM);
        when(rs.getString("external_source_event_id")).thenReturn("CARD_VEHICLES_USED:CVU-1:" + lifecycle);
        when(rs.getString("event_type")).thenReturn(eventType);
        when(rs.getString("lifecycle")).thenReturn(lifecycle);
        when(rs.getObject("card_slot")).thenReturn(0);
        return rs;
    }

    private ResultSet commonRow(String sourceRowId, String occurredAt, Long odometerM) throws SQLException {
        ResultSet rs = mock(ResultSet.class);
        when(rs.getString("source_row_id")).thenReturn(sourceRowId);
        when(rs.getObject("occurred_at")).thenReturn(OffsetDateTime.parse(occurredAt));
        when(rs.getObject("received_partner_at")).thenReturn(null);
        when(rs.getObject("odometer_m")).thenReturn(odometerM);

        when(rs.getString("driver_source_entity_id")).thenReturn("driver-1");
        when(rs.getString("driver_card_nation")).thenReturn("A");
        when(rs.getString("driver_card_number")).thenReturn("12345678901234");

        when(rs.getString("vehicle_source_entity_id")).thenReturn("vehicle-1");
        when(rs.getString("vehicle_vin")).thenReturn("VIN123");
        when(rs.getString("vehicle_registration_source_entity_id")).thenReturn("reg-1");
        when(rs.getString("vehicle_registration_nation")).thenReturn("A");
        when(rs.getString("vehicle_registration_number")).thenReturn("W-12345");

        when(rs.getString("source_package_kind")).thenReturn("DRIVER_CARD");
        when(rs.getString("source_package_id")).thenReturn("package-1");
        when(rs.getString("source_package_entity_id")).thenReturn("card-1");
        when(rs.getObject("source_package_period_from")).thenReturn(null);
        when(rs.getObject("source_package_period_to")).thenReturn(null);
        when(rs.getObject("source_package_imported_at")).thenReturn(null);
        return rs;
    }

    private ExtractionContext<TachographImportRequest> context(EventFamily family, String sourceKind, String extractionCode) {
        return new ExtractionContext<>(
                UUID.randomUUID(),
                UUID.randomUUID(),
                1,
                null,
                new ImportPlanItemDto(
                        family,
                        sourceKind,
                        extractionCode,
                        List.of("source-table"),
                        "DRIVER",
                        "test extraction",
                        AcquisitionStrategy.OCCURRED_AT_WINDOW_WITH_OVERLAP
                ),
                new ImportTimeChunkDto(0, null, null),
                new EventSourceDto("TACHOGRAPH", "MIXED", "TACHOGRAPH_DB", "test", null, null),
                null
        );
    }
}
