package at.procon.eventhub.processing.service;

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

import at.procon.eventhub.config.EventHubProperties;
import at.procon.eventhub.processing.model.UnifiedDiscoveredVehicleRef;
import at.procon.eventhub.processing.model.UnifiedRuntimeProcessingRequest;
import at.procon.eventhub.service.EventAcquisitionRecordKeyService;
import at.procon.eventhub.service.EventDetailsFactory;
import at.procon.eventhub.service.EventHubEventSorter;
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
import at.procon.eventhub.tachographfilesession.model.ExtractedCardActivityInterval;
import at.procon.eventhub.tachographfilesession.model.ExtractedCardVehicleUsageInterval;
import at.procon.eventhub.tachographfilesession.model.ExtractedDriver;
import at.procon.eventhub.tachographfilesession.model.ExtractedDriverCard;
import at.procon.eventhub.tachographfilesession.model.ExtractedSupportEvent;
import at.procon.eventhub.tachographfilesession.model.ExtractedVehicle;
import at.procon.eventhub.tachographfilesession.model.ExtractedVehicleRegistration;
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
import at.procon.eventhub.tachographfilesession.service.DriverKeyFactory;
import at.procon.eventhub.tachographfilesession.service.DriverTimelineBuilder;
import at.procon.eventhub.tachographfilesession.model.TachographCompositeSession;
import at.procon.eventhub.tachographfilesession.service.InMemoryTachographCompositeSessionRepository;
import at.procon.eventhub.tachographfilesession.service.InMemoryTachographFileSessionRepository;
import at.procon.eventhub.tachographfilesession.service.IntervalBackedDriverTimelineEventBuilder;
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionRepository;
import at.procon.eventhub.tachographfilesession.service.VehicleKeyFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.jupiter.api.Test;

class TachographFileSessionRuntimeEventLoaderTest {

    @Test
    void loadsDriverAndVehicleEventsFromFileSessionRuntimePath() {
        EventHubProperties properties = new EventHubProperties();
        TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
        InMemoryTachographCompositeSessionRepository compositeRepository = new InMemoryTachographCompositeSessionRepository();
        IntervalBackedDriverTimelineEventBuilder eventBuilder = new IntervalBackedDriverTimelineEventBuilder(
                new DriverTimelineBuilder(),
                new DriverKeyFactory(),
                new VehicleKeyFactory(),
                new EventDetailsFactory(new ObjectMapper())
        );
        TachographFileSessionRuntimeEventLoader loader = new TachographFileSessionRuntimeEventLoader(
                new UnifiedDriverEventSourceService(List.of(new TachographFileSessionUnifiedDriverEventSource(repository, eventBuilder))),
                new UnifiedVehicleEventSourceService(List.of(new TachographFileSessionUnifiedVehicleEventSource(repository, eventBuilder))),
                compositeRepository,
                new EventAcquisitionRecordKeyService(),
                new EventHubEventSorter()
        );

        DriverExtractionSession driver = driver();
        TachographFileSession session = session(driver);
        repository.save(session);

        UnifiedRuntimeProcessingRequest request = UnifiedRuntimeProcessingRequest.forTachographFileSession(
                session.sessionId(),
                driver.driverKey(),
                OffsetDateTime.parse("2026-05-01T08:00:00Z"),
                OffsetDateTime.parse("2026-05-01T10:00:00Z"),
                true,
                0
        );

        assertThat(loader.supports(request, at.procon.eventhub.processing.model.UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION)).isTrue();
        assertThat(loader.loadDriverEvents(request)).hasSize(5);
        assertThat(loader.loadVehicleEvents(request, new UnifiedDiscoveredVehicleRef("VIN-1", "VIN-1", "12", "REG-1"))).hasSize(5);
    }



    @Test
    void loadsDriverAndVehicleEventsFromCompositeSessionRuntimePath() {
        EventHubProperties properties = new EventHubProperties();
        TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
        InMemoryTachographCompositeSessionRepository compositeRepository = new InMemoryTachographCompositeSessionRepository();
        IntervalBackedDriverTimelineEventBuilder eventBuilder = new IntervalBackedDriverTimelineEventBuilder(
                new DriverTimelineBuilder(),
                new DriverKeyFactory(),
                new VehicleKeyFactory(),
                new EventDetailsFactory(new ObjectMapper())
        );
        TachographFileSessionRuntimeEventLoader loader = new TachographFileSessionRuntimeEventLoader(
                new UnifiedDriverEventSourceService(List.of(new TachographFileSessionUnifiedDriverEventSource(repository, eventBuilder))),
                new UnifiedVehicleEventSourceService(List.of(new TachographFileSessionUnifiedVehicleEventSource(repository, eventBuilder))),
                compositeRepository,
                new EventAcquisitionRecordKeyService(),
                new EventHubEventSorter()
        );

        DriverExtractionSession firstDriverSession = driver();
        DriverExtractionSession secondDriverSession = driverOnSecondDay();
        TachographFileSession firstSession = session(firstDriverSession);
        TachographFileSession secondSession = session(secondDriverSession);
        repository.save(firstSession);
        repository.save(secondSession);
        UUID compositeSessionId = UUID.randomUUID();
        compositeRepository.save(new TachographCompositeSession(
                compositeSessionId,
                "default",
                "runtime-composite",
                List.of(firstSession.sessionId(), secondSession.sessionId()),
                Instant.now()
        ));

        UnifiedRuntimeProcessingRequest request = UnifiedRuntimeProcessingRequest.forTachographCompositeSession(
                compositeSessionId,
                firstDriverSession.driverKey(),
                OffsetDateTime.parse("2026-05-01T00:00:00Z"),
                OffsetDateTime.parse("2026-05-03T00:00:00Z"),
                true,
                0
        );

        assertThat(loader.loadDriverEvents(request)).hasSize(10);
        assertThat(loader.loadVehicleEvents(request, new UnifiedDiscoveredVehicleRef("VIN-1", "VIN-1", "12", "REG-1"))).hasSize(10);
    }


    private DriverExtractionSession driver() {
        return new DriverExtractionSession(
                "12:123",
                new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null),
                new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null),
                List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")),
                List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")),
                List.of(new ExtractedCardVehicleUsageInterval(
                        "CVU-1",
                        OffsetDateTime.parse("2026-05-01T08:00:00Z"),
                        OffsetDateTime.parse("2026-05-01T10:00:00Z"),
                        100L,
                        200L,
                        "12:REG-1",
                        "VIN-1",
                        "vu-1"
                )),
                List.of(new ExtractedCardActivityInterval(
                        "ACT-1",
                        OffsetDateTime.parse("2026-05-01T08:30:00Z"),
                        OffsetDateTime.parse("2026-05-01T09:00:00Z"),
                        "DRIVE",
                        "DRIVER",
                        "INSERTED",
                        "SINGLE",
                        "12:REG-1",
                        "VIN-1",
                        "a"
                )),
                List.of(new ExtractedSupportEvent(
                        "SUP-1",
                        "12:123",
                        OffsetDateTime.parse("2026-05-01T08:45:00Z"),
                        "POSITION",
                        "POSITION_RECORDED",
                        "SNAPSHOT",
                        "DRIVER",
                        "12:REG-1",
                        "VIN-1",
                        null,
                        null,
                        null,
                        null,
                        null,
                        BigDecimal.valueOf(48.2082),
                        BigDecimal.valueOf(16.3738),
                        "AUTHENTIC",
                        150L,
                        null,
                        null,
                        null,
                        "raw-path"
                )),
                List.of()
        );
    }



    private DriverExtractionSession driverOnSecondDay() {
        return new DriverExtractionSession(
                "12:123",
                new ExtractedDriver("12:123", "DRV:12:123", "Doe", "Jane", null, null, null, null, null),
                new ExtractedDriverCard("CARD:12:123", "12", "123", "123", null, null, null, null),
                List.of(new ExtractedVehicleRegistration("12:REG-1", "VR:12:REG-1", "12", "REG-1")),
                List.of(new ExtractedVehicle("VIN-1", "VIN:VIN-1", "VIN-1")),
                List.of(new ExtractedCardVehicleUsageInterval(
                        "CVU-2",
                        OffsetDateTime.parse("2026-05-02T08:00:00Z"),
                        OffsetDateTime.parse("2026-05-02T10:00:00Z"),
                        200L,
                        300L,
                        "12:REG-1",
                        "VIN-1",
                        "vu-2"
                )),
                List.of(new ExtractedCardActivityInterval(
                        "ACT-2",
                        OffsetDateTime.parse("2026-05-02T08:30:00Z"),
                        OffsetDateTime.parse("2026-05-02T09:00:00Z"),
                        "DRIVE",
                        "DRIVER",
                        "INSERTED",
                        "SINGLE",
                        "12:REG-1",
                        "VIN-1",
                        "a2"
                )),
                List.of(new ExtractedSupportEvent(
                        "SUP-2",
                        "12:123",
                        OffsetDateTime.parse("2026-05-02T08:45:00Z"),
                        "POSITION",
                        "POSITION_RECORDED",
                        "SNAPSHOT",
                        "DRIVER",
                        "12:REG-1",
                        "VIN-1",
                        null,
                        null,
                        null,
                        null,
                        null,
                        BigDecimal.valueOf(48.2082),
                        BigDecimal.valueOf(16.3738),
                        "AUTHENTIC",
                        250L,
                        null,
                        null,
                        null,
                        "raw-path-2"
                )),
                List.of()
        );
    }


    private TachographFileSession session(DriverExtractionSession driver) {
        return new TachographFileSession(
                UUID.randomUUID(),
                new TachographFileSessionMetadata(
                        "default",
                        "legalrequirements-drivercard",
                        "sample",
                        "sample.ddd",
                        "a",
                        2,
                        "42",
                        "b",
                        true,
                        null
                ),
                Map.of(driver.driverKey(), driver),
                new ExtractionStats(1, 1, 1, 1, 1, 0),
                List.of(),
                Instant.now(),
                Instant.now().plus(4, ChronoUnit.HOURS)
        );
    }
}
