package at.procon.eventhub.processing.service;

import at.procon.eventhub.dto.EventHubEventDto;
import at.procon.eventhub.dto.VehicleRefDto;
import at.procon.eventhub.processing.model.UnifiedEventSourceFamily;
import at.procon.eventhub.processing.model.UnifiedVehicleEventsRequest;
import at.procon.eventhub.reference.TachographNationRegistry;
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.service.DriverTimelineEventBuilder;
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionNotFoundException;
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionRepository;
import java.time.OffsetDateTime;
import java.util.List;
import org.springframework.stereotype.Component;

@Component
public class TachographFileSessionUnifiedVehicleEventSource implements UnifiedVehicleEventSource {

    private final TachographFileSessionRepository repository;
    private final DriverTimelineEventBuilder eventBuilder;

    public TachographFileSessionUnifiedVehicleEventSource(
            TachographFileSessionRepository repository,
            DriverTimelineEventBuilder eventBuilder
    ) {
        this.repository = repository;
        this.eventBuilder = eventBuilder;
    }

    @Override
    public boolean supports(UnifiedVehicleEventsRequest request) {
        return request.sourceFamily() == UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION;
    }

    @Override
    public List<EventHubEventDto> loadVehicleEvents(UnifiedVehicleEventsRequest request) {
        TachographFileSession session = repository.find(request.sessionId())
                .orElseThrow(() -> new TachographFileSessionNotFoundException(request.sessionId()));
        return session.driversByKey().values().stream()
                .flatMap(driver -> eventBuilder.buildEvents(session, driver).stream())
                .filter(event -> matchesVehicle(event.vehicleRef(), request))
                .filter(event -> withinWindow(event.occurredAt(), request.occurredFrom(), request.occurredTo()))
                .distinct()
                .toList();
    }

    private boolean matchesVehicle(VehicleRefDto vehicleRef, UnifiedVehicleEventsRequest request) {
        if (vehicleRef == null || !vehicleRef.hasAnyReference()) {
            return false;
        }
        if (request.vehicleSourceEntityId() != null
                && request.vehicleSourceEntityId().equals(vehicleRef.sourceVehicleEntityId())) {
            return true;
        }
        if (request.vin() != null && request.vin().equals(vehicleRef.vin())) {
            return true;
        }
        return request.registrationNumber() != null
                && vehicleRef.vehicleRegistration() != null
                && request.registrationNumber().equals(vehicleRef.vehicleRegistration().number())
                && matchesNation(request.registrationNation(), vehicleRef.vehicleRegistration().nation(), vehicleRef.vehicleRegistration().nationNumericCode());
    }

    private boolean withinWindow(
            OffsetDateTime occurredAt,
            OffsetDateTime occurredFrom,
            OffsetDateTime occurredTo
    ) {
        if (occurredAt == null) {
            return false;
        }
        if (occurredFrom != null && occurredAt.isBefore(occurredFrom)) {
            return false;
        }
        return occurredTo == null || !occurredAt.isAfter(occurredTo);
    }

    private boolean matchesNation(String requestedNation, String actualNation, Integer actualNationNumericCode) {
        if (requestedNation == null) {
            return true;
        }
        TachographNationRegistry.NationResolution requested = TachographNationRegistry.resolve(requestedNation, null);
        TachographNationRegistry.NationResolution actual = TachographNationRegistry.resolve(actualNation, actualNationNumericCode);
        if (requested.numericCode() != null || actual.numericCode() != null) {
            return requested.numericCode() != null
                    && actual.numericCode() != null
                    && requested.numericCode().equals(actual.numericCode());
        }
        return requested.legacyNation() != null && requested.legacyNation().equals(actual.legacyNation());
    }
}
