package at.procon.eventhub.processing.service;

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

import at.procon.eventhub.dto.DriverRefDto;
import at.procon.eventhub.dto.EventDomain;
import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.dto.EventHubPackageRequest;
import at.procon.eventhub.dto.EventLifecycle;
import at.procon.eventhub.dto.EventSourceDto;
import at.procon.eventhub.dto.EventType;
import at.procon.eventhub.dto.ImportScopeDto;
import at.procon.eventhub.dto.VehicleRefDto;
import at.procon.eventhub.dto.VehicleRegistrationRefDto;
import at.procon.eventhub.processing.model.UnifiedDiscoveredVehicleRef;
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
import at.procon.eventhub.processing.model.UnifiedRuntimeEventBackend;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.tachographfilesession.model.ResolvedDriverTimeline;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.junit.jupiter.api.Test;

class UnifiedRuntimeDriverTimelineServiceTest {

    @Test
    void reconstructsTimelineFromRuntimeAssembledEvents() {
        UnifiedRuntimeEventAssemblyService assemblyService = new UnifiedRuntimeEventAssemblyService(
                List.of(new FakeRuntimeLoader()),
                List.of(new FakeRuntimeLoader())
        );
        UnifiedRuntimeDriverTimelineService service = new UnifiedRuntimeDriverTimelineService(
                assemblyService,
                new UnifiedEventTimelineReconstructor()
        );

        ResolvedDriverTimeline timeline = service.loadDriverTimeline(
                new UnifiedRuntimeProcessingRequest(
                        null,
                        List.of(),
                        null,
                        "default",
                        Set.of(UnifiedEventSourceFamily.TACHOGRAPH_DB),
                        UnifiedRuntimeEventBackend.SOURCE_DB,
                        null,
                        Set.of(),
                        false,
                        Set.of(),
                        false,
                        "DRIVER:42",
                        null,
                        null,
                        OffsetDateTime.parse("2026-05-01T00:00:00Z"),
                        OffsetDateTime.parse("2026-05-02T00:00:00Z"),
                        false,
                        0
                )
        );

        assertThat(timeline.sourceKind()).isEqualTo("DRIVER_CARD");
        assertThat(timeline.loadedFrom()).isEqualTo(OffsetDateTime.parse("2026-05-01T08:00:00Z"));
        assertThat(timeline.loadedTo()).isEqualTo(OffsetDateTime.parse("2026-05-01T11:00:00Z"));
        assertThat(timeline.activityIntervals()).hasSize(1);
        assertThat(timeline.activityIntervals().get(0).intervalId()).isEqualTo("ACT-1");
        assertThat(timeline.vehicleUsageIntervals()).hasSize(1);
        assertThat(timeline.vehicleUsageIntervals().get(0).intervalId()).isEqualTo("CVU-1");
        assertThat(timeline.supportEvents()).isEmpty();
    }

    private static final class FakeRuntimeLoader implements RuntimeDriverEventLoader, RuntimeVehicleEventLoader {

        @Override
        public boolean supports(UnifiedRuntimeProcessingRequest request, UnifiedEventSourceFamily sourceFamily) {
            return request.eventBackend() == UnifiedRuntimeEventBackend.SOURCE_DB
                    && sourceFamily == UnifiedEventSourceFamily.TACHOGRAPH_DB;
        }

        @Override
        public List<EventHubEventDto> loadDriverEvents(UnifiedRuntimeProcessingRequest request) {
            return List.of(
                    activityEvent("ACT-1", EventLifecycle.START, "2026-05-01T08:00:00Z"),
                    vehicleUsageEvent("CVU-1", EventType.CARD_INSERTED, EventLifecycle.INSERT, "2026-05-01T08:00:00Z", 100_000L),
                    activityEvent("ACT-1", EventLifecycle.END, "2026-05-01T09:30:00Z"),
                    vehicleUsageEvent("CVU-1", EventType.CARD_WITHDRAWN, EventLifecycle.WITHDRAW, "2026-05-01T11:00:00Z", 160_000L)
            );
        }

        @Override
        public List<EventHubEventDto> loadVehicleEvents(
                UnifiedRuntimeProcessingRequest request,
                UnifiedDiscoveredVehicleRef vehicleRef
        ) {
            return List.of();
        }

        private EventHubEventDto activityEvent(String intervalId, EventLifecycle lifecycle, String occurredAt) {
            ObjectNode raw = JsonNodeFactory.instance.objectNode();
            raw.put("intervalId", intervalId);
            raw.put("registrationKey", "12:REG-1");
            raw.put("vehicleKey", "VIN-1");
            raw.put("sourceKind", "DRIVER_CARD");
            ObjectNode payload = JsonNodeFactory.instance.objectNode();
            payload.set("raw", raw);
            return new EventHubEventDto(
                    UUID.randomUUID(),
                    intervalId + ":" + lifecycle.name(),
                    new DriverRefDto("DRIVER:42", null),
                    vehicleRef(),
                    OffsetDateTime.parse(occurredAt),
                    null,
                    OffsetDateTime.parse(occurredAt),
                    EventDomain.DRIVER_ACTIVITY,
                    EventType.DRIVE,
                    lifecycle,
                    null,
                    null,
                    null,
                    null,
                    payload,
                    false,
                    packageInfo(EventDomain.DRIVER_ACTIVITY)
            );
        }

        private EventHubEventDto vehicleUsageEvent(
                String intervalId,
                EventType eventType,
                EventLifecycle lifecycle,
                String occurredAt,
                long odometerM
        ) {
            ObjectNode raw = JsonNodeFactory.instance.objectNode();
            raw.put("intervalId", intervalId);
            raw.put("registrationKey", "12:REG-1");
            raw.put("vehicleKey", "VIN-1");
            raw.put("sourceKind", "DRIVER_CARD");
            ObjectNode payload = JsonNodeFactory.instance.objectNode();
            payload.set("raw", raw);
            return new EventHubEventDto(
                    UUID.randomUUID(),
                    intervalId + ":" + eventType.name(),
                    new DriverRefDto("DRIVER:42", null),
                    vehicleRef(),
                    OffsetDateTime.parse(occurredAt),
                    null,
                    OffsetDateTime.parse(occurredAt),
                    EventDomain.DRIVER_CARD,
                    eventType,
                    lifecycle,
                    odometerM,
                    null,
                    null,
                    null,
                    payload,
                    false,
                    packageInfo(EventDomain.DRIVER_CARD)
            );
        }

        private VehicleRefDto vehicleRef() {
            return new VehicleRefDto("VEH-1", "VIN-1", "VR-1", new VehicleRegistrationRefDto("12", 12, "REG-1"));
        }

        private EventHubPackageRequest packageInfo(EventDomain eventDomain) {
            EventSourceDto source = new EventSourceDto("TACHOGRAPH", "DRIVER_CARD", "SOURCE", null, null, null);
            return new EventHubPackageRequest(
                    "default",
                    source,
                    null,
                    ImportScopeDto.tenantAll(
                            OffsetDateTime.parse("2026-05-01T00:00:00Z"),
                            OffsetDateTime.parse("2026-05-02T00:00:00Z")
                    ),
                    eventDomain.name(),
                    LocalDate.parse("2026-05-01"),
                    source.stableKey() + ":" + eventDomain.name() + ":2026-05-01"
            );
        }
    }
}
