package at.procon.eventhub.processing.service;

import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.processing.model.UnifiedDiscoveredVehicleRef;
import at.procon.eventhub.processing.model.UnifiedDriverEventsRequest;
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.processing.model.UnifiedVehicleEventsRequest;
import at.procon.eventhub.service.EventAcquisitionRecordKeyService;
import at.procon.eventhub.service.EventHubEventSorter;
import at.procon.eventhub.tachographfilesession.service.TachographCompositeSessionNotFoundException;
import at.procon.eventhub.tachographfilesession.service.TachographCompositeSessionRepository;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Component;

@Component
public class TachographFileSessionRuntimeEventLoader implements RuntimeDriverEventLoader, RuntimeVehicleEventLoader {

    private final UnifiedDriverEventSourceService driverEventSourceService;
    private final UnifiedVehicleEventSourceService vehicleEventSourceService;
    private final TachographCompositeSessionRepository compositeSessionRepository;
    private final EventAcquisitionRecordKeyService eventKeyService;
    private final EventHubEventSorter eventSorter;

    public TachographFileSessionRuntimeEventLoader(
            UnifiedDriverEventSourceService driverEventSourceService,
            UnifiedVehicleEventSourceService vehicleEventSourceService,
            TachographCompositeSessionRepository compositeSessionRepository,
            EventAcquisitionRecordKeyService eventKeyService,
            EventHubEventSorter eventSorter
    ) {
        this.driverEventSourceService = driverEventSourceService;
        this.vehicleEventSourceService = vehicleEventSourceService;
        this.compositeSessionRepository = compositeSessionRepository;
        this.eventKeyService = eventKeyService;
        this.eventSorter = eventSorter;
    }

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

    @Override
    public List<EventHubEventDto> loadDriverEvents(UnifiedRuntimeProcessingRequest request) {
        List<EventHubEventDto> result = new ArrayList<>();
        for (UUID sessionId : resolveSessionIds(request)) {
            result.addAll(driverEventSourceService.loadDriverEvents(
                    UnifiedDriverEventsRequest.forTachographFileSession(
                            sessionId,
                            request.driverKey(),
                            request.occurredFrom(),
                            request.occurredTo()
                    )
            ));
        }
        return deduplicateBySignatureAndSort(result);
    }

    @Override
    public List<EventHubEventDto> loadVehicleEvents(
            UnifiedRuntimeProcessingRequest request,
            UnifiedDiscoveredVehicleRef vehicleRef
    ) {
        List<EventHubEventDto> result = new ArrayList<>();
        for (UUID sessionId : resolveSessionIds(request)) {
            result.addAll(vehicleEventSourceService.loadVehicleEvents(
                    UnifiedVehicleEventsRequest.forTachographFileSession(
                            sessionId,
                            vehicleRef.sourceVehicleEntityId(),
                            vehicleRef.vin(),
                            vehicleRef.registrationNation(),
                            vehicleRef.registrationNumber(),
                            request.vehicleOccurredFrom(),
                            request.vehicleOccurredTo()
                    )
            ));
        }
        return deduplicateBySignatureAndSort(result);
    }

    private List<UUID> resolveSessionIds(UnifiedRuntimeProcessingRequest request) {
        if (request.compositeSessionId() != null) {
            return compositeSessionRepository.find(request.compositeSessionId())
                    .orElseThrow(() -> new TachographCompositeSessionNotFoundException(request.compositeSessionId()))
                    .memberSessionIds();
        }
        return request.sessionIds();
    }

    private List<EventHubEventDto> deduplicateBySignatureAndSort(List<EventHubEventDto> events) {
        LinkedHashMap<String, EventHubEventDto> bySignature = new LinkedHashMap<>();
        for (EventHubEventDto event : events) {
            bySignature.putIfAbsent(eventKeyService.buildEventSignatureHash(event), event);
        }
        return eventSorter.sort(new ArrayList<>(bySignature.values()));
    }
}
