package at.procon.eventhub.processing.model;

import at.procon.eventhub.reference.DriverCardNumberNormalizer;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

public record UnifiedRuntimeProcessingRequest(
        UUID sessionId,
        List<UUID> sessionIds,
        UUID compositeSessionId,
        String tenantKey,
        Set<UnifiedEventSourceFamily> sourceFamilies,
        UnifiedRuntimeEventBackend eventBackend,
        String driverKey,
        Set<String> driverKeys,
        boolean includeAllDrivers,
        Set<String> vehicleKeys,
        boolean includeAllVehicles,
        String driverSourceEntityId,
        String driverCardNation,
        String driverCardNumber,
        OffsetDateTime occurredFrom,
        OffsetDateTime occurredTo,
        boolean expandVehicleEvents,
        int vehicleExpansionPaddingMinutes
) {
    public UnifiedRuntimeProcessingRequest {
        if (sourceFamilies == null || sourceFamilies.isEmpty()) {
            throw new IllegalArgumentException("sourceFamilies must not be empty");
        }
        sourceFamilies = Set.copyOf(sourceFamilies);
        eventBackend = eventBackend == null ? UnifiedRuntimeEventBackend.SOURCE_DB : eventBackend;
        sessionIds = normalizeSessionIds(sessionId, sessionIds);
        if (sessionId == null && !sessionIds.isEmpty()) {
            sessionId = sessionIds.get(0);
        }
        if (compositeSessionId != null && !sessionIds.isEmpty()) {
            throw new IllegalArgumentException("Use either compositeSessionId or sessionId/sessionIds, not both.");
        }
        driverKey = normalize(driverKey);
        driverKeys = normalizeStrings(driverKeys);
        if (driverKey != null) {
            LinkedHashSet<String> mergedDriverKeys = new LinkedHashSet<>(driverKeys);
            mergedDriverKeys.add(driverKey);
            driverKeys = Set.copyOf(mergedDriverKeys);
        }
        if (driverKey == null && driverKeys.size() == 1) {
            driverKey = driverKeys.iterator().next();
        }
        vehicleKeys = normalizeStrings(vehicleKeys);
        tenantKey = normalize(tenantKey);
        driverSourceEntityId = normalize(driverSourceEntityId);
        driverCardNation = normalizeUpper(driverCardNation);
        driverCardNumber = normalizeDriverCardNumber(driverCardNumber);
        boolean includesFileSession = sourceFamilies.contains(UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION);
        boolean includesExternalDb = sourceFamilies.stream()
                .anyMatch(family -> family == UnifiedEventSourceFamily.TACHOGRAPH_DB || family == UnifiedEventSourceFamily.YELLOWFOX_DB);
        if (includesFileSession && eventBackend == UnifiedRuntimeEventBackend.EVENTHUB_DB) {
            throw new IllegalArgumentException("TACHOGRAPH_FILE_SESSION runtime processing currently supports SOURCE_DB backend only.");
        }
        if (tenantKey == null) {
            if (!includesFileSession || includesExternalDb) {
                throw new IllegalArgumentException("tenantKey must not be blank");
            }
        }
        if (includesFileSession && compositeSessionId == null && sessionIds.isEmpty()) {
            throw new IllegalArgumentException("sessionId, sessionIds or compositeSessionId must be provided when TACHOGRAPH_FILE_SESSION is selected.");
        }
        if (includesFileSession && driverKey == null && driverKeys.isEmpty() && !includeAllDrivers) {
            throw new IllegalArgumentException("driverKey, driverKeys or includeAllDrivers must be provided when TACHOGRAPH_FILE_SESSION is selected.");
        }
        if (includesExternalDb && driverSourceEntityId == null && driverCardNumber == null
                && driverKeys.isEmpty() && !includeAllDrivers) {
            throw new IllegalArgumentException("At least one driver selector, driverKeys or includeAllDrivers must be provided.");
        }
        if (includesExternalDb && (includeAllDrivers || !driverKeys.isEmpty())
                && (occurredFrom == null || occurredTo == null)) {
            throw new IllegalArgumentException("occurredFrom and occurredTo are required when loading broad external DB runtime scopes.");
        }
        if (eventBackend == UnifiedRuntimeEventBackend.EVENTHUB_DB
                && includesExternalDb
                && driverSourceEntityId == null
                && driverCardNumber == null
                && (includeAllDrivers || !driverKeys.isEmpty())) {
            throw new IllegalArgumentException("Broad multi-driver EVENTHUB_DB runtime scopes are not supported yet; provide a concrete EventHub driver selector or use SOURCE_DB.");
        }
        if (occurredFrom != null && occurredTo != null && occurredTo.isBefore(occurredFrom)) {
            throw new IllegalArgumentException("occurredTo must not be before occurredFrom");
        }
        if (vehicleExpansionPaddingMinutes < 0) {
            throw new IllegalArgumentException("vehicleExpansionPaddingMinutes must not be negative");
        }
    }

    public static UnifiedRuntimeProcessingRequest forDriver(
            String tenantKey,
            Set<UnifiedEventSourceFamily> sourceFamilies,
            String driverSourceEntityId,
            String driverCardNation,
            String driverCardNumber,
            OffsetDateTime occurredFrom,
            OffsetDateTime occurredTo
    ) {
        return forDriver(
                tenantKey,
                sourceFamilies,
                driverSourceEntityId,
                driverCardNation,
                driverCardNumber,
                occurredFrom,
                occurredTo,
                UnifiedRuntimeEventBackend.SOURCE_DB,
                true,
                0
        );
    }

    public static UnifiedRuntimeProcessingRequest forDriver(
            String tenantKey,
            Set<UnifiedEventSourceFamily> sourceFamilies,
            String driverSourceEntityId,
            String driverCardNation,
            String driverCardNumber,
            OffsetDateTime occurredFrom,
            OffsetDateTime occurredTo,
            boolean expandVehicleEvents,
            int vehicleExpansionPaddingMinutes
    ) {
        return forDriver(
                tenantKey,
                sourceFamilies,
                driverSourceEntityId,
                driverCardNation,
                driverCardNumber,
                occurredFrom,
                occurredTo,
                UnifiedRuntimeEventBackend.SOURCE_DB,
                expandVehicleEvents,
                vehicleExpansionPaddingMinutes
        );
    }

    public static UnifiedRuntimeProcessingRequest forDriver(
            String tenantKey,
            Set<UnifiedEventSourceFamily> sourceFamilies,
            String driverSourceEntityId,
            String driverCardNation,
            String driverCardNumber,
            OffsetDateTime occurredFrom,
            OffsetDateTime occurredTo,
            UnifiedRuntimeEventBackend eventBackend,
            boolean expandVehicleEvents,
            int vehicleExpansionPaddingMinutes
    ) {
        return new UnifiedRuntimeProcessingRequest(
                null,
                List.of(),
                null,
                tenantKey,
                sourceFamilies,
                eventBackend,
                null,
                Set.of(),
                false,
                Set.of(),
                false,
                driverSourceEntityId,
                driverCardNation,
                driverCardNumber,
                occurredFrom,
                occurredTo,
                expandVehicleEvents,
                vehicleExpansionPaddingMinutes
        );
    }

    public static UnifiedRuntimeProcessingRequest forDriverFromEventHub(
            String tenantKey,
            Set<UnifiedEventSourceFamily> sourceFamilies,
            String driverSourceEntityId,
            String driverCardNation,
            String driverCardNumber,
            OffsetDateTime occurredFrom,
            OffsetDateTime occurredTo,
            boolean expandVehicleEvents,
            int vehicleExpansionPaddingMinutes
    ) {
        return new UnifiedRuntimeProcessingRequest(
                null,
                List.of(),
                null,
                tenantKey,
                sourceFamilies,
                UnifiedRuntimeEventBackend.EVENTHUB_DB,
                null,
                Set.of(),
                false,
                Set.of(),
                false,
                driverSourceEntityId,
                driverCardNation,
                driverCardNumber,
                occurredFrom,
                occurredTo,
                expandVehicleEvents,
                vehicleExpansionPaddingMinutes
        );
    }

    public static UnifiedRuntimeProcessingRequest forTachographFileSession(
            UUID sessionId,
            String driverKey,
            OffsetDateTime occurredFrom,
            OffsetDateTime occurredTo,
            boolean expandVehicleEvents,
            int vehicleExpansionPaddingMinutes
    ) {
        return new UnifiedRuntimeProcessingRequest(
                sessionId,
                List.of(),
                null,
                null,
                Set.of(UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION),
                UnifiedRuntimeEventBackend.SOURCE_DB,
                driverKey,
                Set.of(),
                false,
                Set.of(),
                false,
                null,
                null,
                null,
                occurredFrom,
                occurredTo,
                expandVehicleEvents,
                vehicleExpansionPaddingMinutes
        );
    }

    public static UnifiedRuntimeProcessingRequest forTachographFileSessions(
            List<UUID> sessionIds,
            String driverKey,
            OffsetDateTime occurredFrom,
            OffsetDateTime occurredTo,
            boolean expandVehicleEvents,
            int vehicleExpansionPaddingMinutes
    ) {
        return new UnifiedRuntimeProcessingRequest(
                null,
                sessionIds,
                null,
                null,
                Set.of(UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION),
                UnifiedRuntimeEventBackend.SOURCE_DB,
                driverKey,
                Set.of(),
                false,
                Set.of(),
                false,
                null,
                null,
                null,
                occurredFrom,
                occurredTo,
                expandVehicleEvents,
                vehicleExpansionPaddingMinutes
        );
    }

    public static UnifiedRuntimeProcessingRequest forTachographCompositeSession(
            UUID compositeSessionId,
            String driverKey,
            OffsetDateTime occurredFrom,
            OffsetDateTime occurredTo,
            boolean expandVehicleEvents,
            int vehicleExpansionPaddingMinutes
    ) {
        return new UnifiedRuntimeProcessingRequest(
                null,
                List.of(),
                compositeSessionId,
                null,
                Set.of(UnifiedEventSourceFamily.TACHOGRAPH_FILE_SESSION),
                UnifiedRuntimeEventBackend.SOURCE_DB,
                driverKey,
                Set.of(),
                false,
                Set.of(),
                false,
                null,
                null,
                null,
                occurredFrom,
                occurredTo,
                expandVehicleEvents,
                vehicleExpansionPaddingMinutes
        );
    }

    public OffsetDateTime vehicleOccurredFrom() {
        return occurredFrom == null ? null : occurredFrom.minusMinutes(vehicleExpansionPaddingMinutes);
    }

    public OffsetDateTime vehicleOccurredTo() {
        return occurredTo == null ? null : occurredTo.plusMinutes(vehicleExpansionPaddingMinutes);
    }

    private static List<UUID> normalizeSessionIds(UUID sessionId, List<UUID> sessionIds) {
        LinkedHashSet<UUID> result = new LinkedHashSet<>();
        if (sessionId != null) {
            result.add(sessionId);
        }
        if (sessionIds != null) {
            result.addAll(sessionIds.stream().filter(value -> value != null).toList());
        }
        return List.copyOf(new ArrayList<>(result));
    }

    public UnifiedRuntimeProcessingRequest withDriverKey(String value) {
        return new UnifiedRuntimeProcessingRequest(
                sessionId,
                sessionIds,
                compositeSessionId,
                tenantKey,
                sourceFamilies,
                eventBackend,
                value,
                Set.of(),
                false,
                vehicleKeys,
                includeAllVehicles,
                driverSourceEntityId,
                driverCardNation,
                driverCardNumber,
                occurredFrom,
                occurredTo,
                expandVehicleEvents,
                vehicleExpansionPaddingMinutes
        );
    }

    public boolean scopeDriverSelectionRequested() {
        return includeAllDrivers || driverKeys.size() > 1 || (driverKey == null && !driverKeys.isEmpty());
    }

    private static Set<String> normalizeStrings(Set<String> values) {
        if (values == null || values.isEmpty()) {
            return Set.of();
        }
        LinkedHashSet<String> normalized = new LinkedHashSet<>();
        for (String value : values) {
            String normalizedValue = normalize(value);
            if (normalizedValue != null) {
                normalized.add(normalizedValue);
            }
        }
        return Set.copyOf(normalized);
    }

    private static String normalize(String value) {
        return value == null || value.isBlank() ? null : value.trim();
    }

    private static String normalizeUpper(String value) {
        return value == null || value.isBlank() ? null : value.trim().toUpperCase();
    }

    private static String normalizeDriverCardNumber(String value) {
        return DriverCardNumberNormalizer.canonical(value);
    }
}
