/*
 * Queue - A Queueing system that can be used to handle labs in higher education
 * Copyright (C) 2016-2024  Delft University of Technology
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package nl.tudelft.queue.repository;

import static java.time.LocalDateTime.now;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

import org.springframework.data.domain.*;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import nl.tudelft.labracore.api.dto.AssignmentSummaryDTO;
import nl.tudelft.labracore.lib.security.user.Person;
import nl.tudelft.queue.dto.util.RequestTableFilterDTO;
import nl.tudelft.queue.model.*;
import nl.tudelft.queue.model.enums.Language;
import nl.tudelft.queue.model.enums.RequestType;
import nl.tudelft.queue.model.labs.AbstractSlottedLab;
import nl.tudelft.queue.model.labs.Lab;

public interface LabRequestRepository extends JpaRepository<LabRequest, Long> {

	List<LabRequest> findAllBySessionIdIn(List<Long> list);

	@Query("""
				select request from LabRequest request
				where request.session.id in :#{#labs.![id]}
					and request.assignment in :assignments
					and request.room in :rooms
					and request.requestType in :#{#requestTypes.![#this.name()]}
			""")
	List<LabRequest> findAllByFilters(
			@Param("labs") List<Lab> labs,
			@Param("assignments") List<Long> assignments,
			@Param("rooms") List<Long> rooms,
			@Param("requestTypes") List<RequestType> requestTypes);

	/**
	 * Finds all lab requests that were assigned to an assistant.
	 *
	 * @param  assistantId The assistant id belonging to the assistant in question.
	 * @return             All the labrequests that belonged to the assistant.
	 */
	@Query("""
				select request from LabRequest request
				where request.eventInfo.assignedTo = ?1
			""")
	List<LabRequest> findAllByAssistant(Long assistantId);

	/**
	 * Counts the previous requests from a list of groups after applying a filter.
	 *
	 * @param  groups The groups to search for in made requests
	 * @param  filter The filter to apply to the list of found requestz
	 * @return        The amount of found requests
	 */
	@Query(value = """
				select count(request) from LabRequest request
				where request.studentGroup in :groups
					and (cast(:#{#filter.assigned.isEmpty()} as boolean) or request.eventInfo.assignedTo in :#{#filter.assigned})
					and (cast(:#{#filter.labs.isEmpty()} as boolean) or request.session.id in :#{#filter.labs})
					and (cast(:#{#filter.rooms.isEmpty()} as boolean) or request.room in :#{#filter.rooms})
					and (cast(:#{#filter.onlineModes.isEmpty()} as boolean) or request.onlineMode in :#{#filter.onlineModes.![#this.ordinal()]})
					and (cast(:#{#filter.assignments.isEmpty()} as boolean) or request.assignment in :#{#filter.assignments})
					and (cast(:#{#filter.requestStatuses.isEmpty()} as boolean) or request.eventInfo.status in :#{#filter.requestStatuses.![#this.name()]})
					and (cast(:#{#filter.requestTypes.isEmpty()} as boolean) or request.requestType in :#{#filter.requestTypes.![#this.ordinal()]})
			""")
	long countPreviousRequests(@Param("groups") List<Long> groups,
			@Param("filter") RequestTableFilterDTO filter);

	/**
	 * Finds a page of previous requests from the given list of groups after applying the given filter.
	 *
	 * @param  groups   The groups to search for in made requests.
	 * @param  filter   The filter to apply to the list of found requests.
	 * @param  pageable The pageable containing configurations for displaying the page.
	 * @return          The page of previous requests.
	 */
	@Query(value = """
				select request from LabRequest request
				where request.studentGroup in :groups
					and (cast(:#{#filter.assigned.isEmpty()} as boolean) or request.eventInfo.assignedTo in :#{#filter.assigned})
					and (cast(:#{#filter.labs.isEmpty()} as boolean) or request.session.id in :#{#filter.labs})
					and (cast(:#{#filter.rooms.isEmpty()} as boolean) or request.room in :#{#filter.rooms})
					and (cast(:#{#filter.onlineModes.isEmpty()} as boolean) or request.onlineMode in :#{#filter.onlineModes.![#this.ordinal()]})
					and (cast(:#{#filter.assignments.isEmpty()} as boolean) or request.assignment in :#{#filter.assignments})
					and (cast(:#{#filter.requestStatuses.isEmpty()} as boolean) or request.eventInfo.status in :#{#filter.requestStatuses.![#this.name()]})
					and (cast(:#{#filter.requestTypes.isEmpty()} as boolean) or request.requestType in :#{#filter.requestTypes.![#this.ordinal()]})
			""")
	Page<LabRequest> findAllPreviousRequests(@Param("groups") List<Long> groups,
			@Param("filter") RequestTableFilterDTO filter, Pageable pageable);

	/**
	 * Finds all previous requests that have to do with the same assignment as the given request.
	 *
	 * @param  request The request to find historically similar requests for.
	 * @return         The sorted list of all previous related requests.
	 */
	@Query("""
				select request from LabRequest request
				where request.assignment = :#{#forRequest.assignment}
					and request.createdAt < :#{#forRequest.createdAt}
					and request.studentGroup = :#{#forRequest.studentGroup}
				order by request.createdAt asc
			""")
	List<LabRequest> findAllPreviousRequests(@Param("forRequest") LabRequest request);

	/**
	 * Finds the request that is currently being processed by the given assistant in the given lab if any
	 * exists.
	 *
	 * @param  assistant The assistant for which to look up the request.
	 * @param  lab       The lab in which to find the request.
	 * @return           Either the currently processing request or nothing.
	 */
	@Query("""
				select request from LabRequest request
				where request.session.id = :#{#lab.id}
					and request.eventInfo.status = 'PROCESSING'
					and request.eventInfo.assignedTo = :#{#assistant.id}
			""")
	Optional<LabRequest> findCurrentlyProcessingRequest(@Param("assistant") Person assistant,
			@Param("lab") Lab lab);

	/**
	 * Finds the request that is currently being processed by the given assistant in the given slot if any
	 * exists.
	 *
	 * @param  assistant The assistant for which to look up the request.
	 * @param  slot      The slot in which to find the request.
	 * @return           Either the currently processing request or nothing.
	 */
	@Query("""
				select request from LabRequest request
				where request.timeSlot.id = :#{#timeslot.id}
					and request.eventInfo.status = 'PROCESSING'
					and request.eventInfo.assignedTo = :#{#assistant.id}
			""")
	Optional<LabRequest> findCurrentlyProcessingRequest(@Param("assistant") Person assistant,
			@Param("timeslot") TimeSlot slot);

	/**
	 * Finds the first request that is currently specifically forwarded to the given assistant within the
	 * given lab. This is useful for determining what request should be handled next.
	 *
	 * @param  assistant The assistant that the request should be forwarded to.
	 * @param  lab       The lab in which the request exists.
	 * @return           The forwarded request or none if no such request exists.
	 */
	@Query("""
				select request from LabRequest request
				where request.session.id = :#{#lab.id}
					and request.eventInfo.status = 'FORWARDED'
					and request.eventInfo.forwardedTo = :#{#assistant.id}
				order by request.eventInfo.lastEventAt
				limit 1
			""")
	Optional<LabRequest> findCurrentlyForwardedRequest(@Param("assistant") Person assistant,
			@Param("lab") Lab lab);

	/**
	 * Finds the first request that is currently specifically forwarded to the given assistant within the
	 * given time slot.
	 *
	 * @param  assistant The assistant that is getting a next request.
	 * @param  slot      The slot for which the assistant is getting the next request.
	 * @return           The found request or none if no such request exists.
	 */
	@Query("""
				select request from LabRequest request
				where request.timeSlot.id = :#{#timeslot.id}
					and request.eventInfo.status = 'FORWARDED'
					and request.eventInfo.forwardedTo = :#{#assistant.id}
				order by request.eventInfo.lastEventAt
				limit 1
			""")
	Optional<LabRequest> findCurrentlyForwardedRequest(Person assistant, @Param("timeslot") TimeSlot slot);

	/**
	 * Finds all next requests for the given exam time slot.
	 *
	 * @param  timeSlot  The time slot to get next requests from.
	 * @param  assistant The assistant that is getting the next request.
	 * @return           All requests that can be picked next in the given time slot.
	 */
	@Deprecated
	@Query("""
				select request from LabRequest request
				where request.timeSlot.id = :#{#timeslot.id}
					and (request.eventInfo.status = 'PENDING'
						or (request.eventInfo.status = 'FORWARDED'
							and request.eventInfo.forwardedTo is null
							and request.eventInfo.forwardedBy <> :#{#assistant.id}))
			""")
	List<LabRequest> findNextExamRequests(@Param("timeslot") ClosableTimeSlot timeSlot,
			@Param("assistant") Person assistant);

	/**
	 * Counts the number of requests the assistant can grab next for the given time slot.
	 *
	 * @param  timeSlot  The id of the time slot the assistant would get the next request from.
	 * @param  assistant The assistant that is fetching the next request.
	 * @return           The number of requests that can be picked from the time slot.
	 */
	@Deprecated
	@Query("""
				select count(request) from LabRequest request
				where request.timeSlot.id = :timeslot
					and (request.eventInfo.status = 'PENDING'
						or (request.eventInfo.status = 'FORWARDED'
							and request.eventInfo.forwardedTo is null
							and request.eventInfo.forwardedBy <> :#{#assistant.id}))
			""")
	long countNextExamRequests(@Param("timeslot") Long timeSlot, @Param("assistant") Person assistant);

	/**
	 * Finds the next request within a timeslotted lab that has the PENDING or FORWARDED (to any) status. This
	 * request will be assigned to the assistant querying for a new request to handle. A timeslotted request
	 * can be taken if its slot is open or if its slot is 10 minutes from now.
	 *
	 * @param  lab                The timeslotted lab from which the next request will be taken.
	 * @param  assistant          The assistant that is getting the next request.
	 * @param  filter             The filter to apply to the request table query.
	 * @param  allowedAssignments The assignments which the assistant is allowed to get.
	 * @param  language           The assistant's language choice
	 * @return                    The next slotted request from the given lab.
	 */
	default Optional<LabRequest> findNextSlotRequest(
			@Param("lab") AbstractSlottedLab<?> lab,
			@Param("assistant") Person assistant,
			@Param("filter") RequestTableFilterDTO filter,
			@Param("allowedAssignments") List<AssignmentSummaryDTO> allowedAssignments,
			@Param("language") Language language) {
		return findNextSlotRequest(lab, assistant, filter,
				allowedAssignments.stream().map(AssignmentSummaryDTO::getId).toList(), language,
				now().plusMinutes(lab.getEarlyOpenTime()));
	}

	@Query(value = """
				select request from LabRequest request
				where request.session.id = :#{#lab.id}
					and request.assignment in :allowedAssignments
					and (request.eventInfo.status = 'PENDING'
						or (request.eventInfo.status = 'FORWARDED'
							and request.eventInfo.forwardedTo is null
							and request.eventInfo.forwardedBy <> :#{#assistant.id}))
					and request.timeSlot.slot.opensAt < :slotShouldBeOpenAt
					and (cast(:#{#filter.assigned.isEmpty()} as boolean) or request.eventInfo.assignedTo in :#{#filter.assigned})
					and (cast(:#{#filter.labs.isEmpty()} as boolean) or request.session.id in :#{#filter.labs})
					and (cast(:#{#filter.rooms.isEmpty()} as boolean) or request.room in :#{#filter.rooms})
					and (cast(:#{#filter.onlineModes.isEmpty()} as boolean) or request.onlineMode in :#{#filter.onlineModes.![#this.ordinal()]})
					and (cast(:#{#filter.assignments.isEmpty()} as boolean) or request.assignment in :#{#filter.assignments})
					and (cast(:#{#filter.requestStatuses.isEmpty()} as boolean) or request.eventInfo.status in :#{#filter.requestStatuses.![#this.name()]})
					and (cast(:#{#filter.requestTypes.isEmpty()} as boolean) or request.requestType in :#{#filter.requestTypes.![#this.ordinal()]})
					and (:#{#language.name()} = 'ANY'
						or (:#{#language.name()} = 'DUTCH_ONLY' and (request.language = 'ANY' or request.language = 'DUTCH_ONLY'))
						or (:#{#language.name()} = 'ENGLISH_ONLY' and (request.language = 'ANY' or request.language = 'ENGLISH_ONLY')))
				order by request.timeSlot.slot.opensAt asc
				limit 1
			""")
	Optional<LabRequest> findNextSlotRequest(
			@Param("lab") AbstractSlottedLab<?> lab,
			@Param("assistant") Person assistant,
			@Param("filter") RequestTableFilterDTO filter,
			@Param("allowedAssignments") List<Long> allowedAssignments,
			@Param("language") Language language,
			@Param("slotShouldBeOpenAt") LocalDateTime slotShouldBeOpenAt);

	/**
	 * Counts the number of slotted requests currently open in the given lab from the viewpoint of the given
	 * Person applying the given filter.
	 *
	 * @param  lab                The lab to find the number of open slotted requests for.
	 * @param  assistant          The assistant requesting the number of open requests.
	 * @param  filter             The fitler the assistant is applying to their requests view.
	 * @param  allowedAssignments The assignments which the assistant is allowed to get.
	 * @param  language           The assistant's language choice
	 * @return                    The count of open slotted requests.
	 */
	default long countOpenSlotRequests(AbstractSlottedLab<?> lab, Person assistant,
			RequestTableFilterDTO filter, List<AssignmentSummaryDTO> allowedAssignments, Language language) {
		return countOpenSlotRequests(lab, assistant, filter,
				allowedAssignments.stream().map(AssignmentSummaryDTO::getId).toList(), language,
				now().plusMinutes(lab.getEarlyOpenTime()));
	}

	@Query(value = """
				select count(request) from LabRequest request
				where request.session.id = :#{#lab.id}
					and request.assignment in :allowedAssignments
					and (request.eventInfo.status = 'PENDING'
						or (request.eventInfo.status = 'FORWARDED'
							and ((request.eventInfo.forwardedTo is null and request.eventInfo.forwardedBy <> :#{#assistant.id})
								or request.eventInfo.forwardedTo = :#{#assistant.id})))
					and request.timeSlot.slot.opensAt < :slotShouldBeOpenAt
					and (cast(:#{#filter.assigned.isEmpty()} as boolean) or request.eventInfo.assignedTo in :#{#filter.assigned})
					and (cast(:#{#filter.labs.isEmpty()} as boolean) or request.session.id in :#{#filter.labs})
					and (cast(:#{#filter.rooms.isEmpty()} as boolean) or request.room in :#{#filter.rooms})
					and (cast(:#{#filter.onlineModes.isEmpty()} as boolean) or request.onlineMode in :#{#filter.onlineModes.![#this.ordinal()]})
					and (cast(:#{#filter.assignments.isEmpty()} as boolean) or request.assignment in :#{#filter.assignments})
					and (cast(:#{#filter.requestStatuses.isEmpty()} as boolean) or request.eventInfo.status in :#{#filter.requestStatuses.![#this.name()]})
					and (cast(:#{#filter.requestTypes.isEmpty()} as boolean) or request.requestType in :#{#filter.requestTypes.![#this.ordinal()]})
					and (:#{#language.name()} = 'ANY'
						or (:#{#language.name()} = 'DUTCH_ONLY' and (request.language = 'ANY' or request.language = 'DUTCH_ONLY'))
						or (:#{#language.name()} = 'ENGLISH_ONLY' and (request.language = 'ANY' or request.language = 'ENGLISH_ONLY')))
			""")
	long countOpenSlotRequests(
			@Param("lab") AbstractSlottedLab<?> lab,
			@Param("assistant") Person assistant,
			@Param("filter") RequestTableFilterDTO filter,
			@Param("allowedAssignments") List<Long> allowedAssignments,
			@Param("language") Language language,
			@Param("slotShouldBeOpenAt") LocalDateTime slotShouldBeOpenAt);

	/**
	 * Finds the next request within a regular lab that has the PENDING or FORWARDED (to any) status. This
	 * request will be assigned to the assistant querying for a new request to handle.
	 *
	 * @param  lab                The lab from which the next request will be taken.
	 * @param  assistant          The assistant that is getting the next request.
	 * @param  filter             The filter to apply to the request table query.
	 * @param  allowedAssignments The assignments which the assistant is allowed to get.
	 * @param  language           The assistant's language choice
	 * @return                    The next request from the given lab.
	 */
	@Query(value = """
				select request from LabRequest request
				where request.session.id = :#{#lab.id}
					and request.assignment in :#{#allowedAssignments.![id]}
					and (request.eventInfo.status = 'PENDING'
						or (request.eventInfo.status = 'FORWARDED'
							and request.eventInfo.forwardedTo is null
							and request.eventInfo.forwardedBy <> :#{#assistant.id}))
					and (cast(:#{#filter.assigned.isEmpty()} as boolean) or request.eventInfo.assignedTo in :#{#filter.assigned})
					and (cast(:#{#filter.labs.isEmpty()} as boolean) or request.session.id in :#{#filter.labs})
					and (cast(:#{#filter.rooms.isEmpty()} as boolean) or request.room in :#{#filter.rooms})
					and (cast(:#{#filter.onlineModes.isEmpty()} as boolean) or request.onlineMode in :#{#filter.onlineModes.![#this.ordinal()]})
					and (cast(:#{#filter.assignments.isEmpty()} as boolean) or request.assignment in :#{#filter.assignments})
					and (cast(:#{#filter.requestStatuses.isEmpty()} as boolean) or request.eventInfo.status in :#{#filter.requestStatuses.![#this.name()]})
					and (cast(:#{#filter.requestTypes.isEmpty()} as boolean) or request.requestType in :#{#filter.requestTypes.![#this.ordinal()]})
					and (:#{#language.name()} = 'ANY'
						or (:#{#language.name()} = 'DUTCH_ONLY' and (request.language = 'ANY' or request.language = 'DUTCH_ONLY'))
						or (:#{#language.name()} = 'ENGLISH_ONLY' and (request.language = 'ANY' or request.language = 'ENGLISH_ONLY')))
				order by request.createdAt asc
				limit 1
			""")
	Optional<LabRequest> findNextNormalRequest(
			@Param("lab") Lab lab,
			@Param("assistant") Person assistant,
			@Param("filter") RequestTableFilterDTO filter,
			@Param("allowedAssignments") List<AssignmentSummaryDTO> allowedAssignments,
			@Param("language") Language language);

	/**
	 * Counts the number of open regular requests in the given lab from the viewpoint of the given assistant
	 * with the given filter applied.
	 *
	 * @param  lab                The lab to count the number of open requests in.
	 * @param  assistant          The assistant requesting this count.
	 * @param  filter             The filter applied by the assistant on the page they are requesting.
	 * @param  allowedAssignments The assignments which the assistant is allowed to get.
	 * @param  language           The assistant's language choice
	 * @return                    The number of regular requests in the given lab.
	 */
	@Query(value = """
				select count(request) from LabRequest request
				where request.session.id = :#{#lab.id}
					and request.assignment in :#{#allowedAssignments.![id]}
					and (request.eventInfo.status = 'PENDING'
						or (request.eventInfo.status = 'FORWARDED'
							and ((request.eventInfo.forwardedTo is null and request.eventInfo.forwardedBy <> :#{#assistant.id})
								or request.eventInfo.forwardedTo = :#{#assistant.id})))
					and (cast(:#{#filter.assigned.isEmpty()} as boolean) or request.eventInfo.assignedTo in :#{#filter.assigned})
					and (cast(:#{#filter.labs.isEmpty()} as boolean) or request.session.id in :#{#filter.labs})
					and (cast(:#{#filter.rooms.isEmpty()} as boolean) or request.room in :#{#filter.rooms})
					and (cast(:#{#filter.onlineModes.isEmpty()} as boolean) or request.onlineMode in :#{#filter.onlineModes.![#this.ordinal()]})
					and (cast(:#{#filter.assignments.isEmpty()} as boolean) or request.assignment in :#{#filter.assignments})
					and (cast(:#{#filter.requestStatuses.isEmpty()} as boolean) or request.eventInfo.status in :#{#filter.requestStatuses.![#this.name()]})
					and (cast(:#{#filter.requestTypes.isEmpty()} as boolean) or request.requestType in :#{#filter.requestTypes.![#this.ordinal()]})
					and (:#{#language.name()} = 'ANY'
						or (:#{#language.name()} = 'DUTCH_ONLY' and (request.language = 'ANY' or request.language = 'DUTCH_ONLY'))
						or (:#{#language.name()} = 'ENGLISH_ONLY' and (request.language = 'ANY' or request.language = 'ENGLISH_ONLY')))
			""")
	long countNormalRequests(
			@Param("lab") Lab lab,
			@Param("assistant") Person assistant,
			@Param("filter") RequestTableFilterDTO filter,
			@Param("allowedAssignments") List<AssignmentSummaryDTO> allowedAssignments,
			@Param("language") Language language);

	/**
	 * Finds all requests that pass the given filter. This filter is a DTO transferred from the page
	 * requesting to see these requests. The filter contains all labs, rooms, assignments, etc. that need to
	 * be displayed. If a field in the filter is left as an empty set, the filter ignores it. Additionally,
	 * returns only past and upcoming requests.
	 *
	 * @param  labs     The labs that the should also be kept in the filter.
	 * @param  filter   The filter to apply to the boolean expression.
	 * @param  language The assistant's language choice
	 * @return          The filtered list of requests.
	 */
	@Query(value = """
				select request from LabRequest request
				left join request.timeSlot timeSlot
				where request.session.id in :#{#labs.![id]}
					and (request.session.type = 'REGULAR'
						or ((request.session.type = 'SLOTTED' or request.session.type = 'EXAM') and timeSlot.slot.opensAt < now()))
					and (cast(:#{#filter.assigned.isEmpty()} as boolean) or request.eventInfo.assignedTo in :#{#filter.assigned})
					and (cast(:#{#filter.labs.isEmpty()} as boolean) or request.session.id in :#{#filter.labs})
					and (cast(:#{#filter.rooms.isEmpty()} as boolean) or request.room in :#{#filter.rooms})
					and (cast(:#{#filter.onlineModes.isEmpty()} as boolean) or request.onlineMode in :#{#filter.onlineModes.![#this.ordinal()]})
					and (cast(:#{#filter.assignments.isEmpty()} as boolean) or request.assignment in :#{#filter.assignments})
					and (cast(:#{#filter.requestStatuses.isEmpty()} as boolean) or request.eventInfo.status in :#{#filter.requestStatuses.![#this.name()]})
					and (cast(:#{#filter.requestTypes.isEmpty()} as boolean) or request.requestType in :#{#filter.requestTypes.![#this.ordinal()]})
					and (:#{#language.name()} = 'ANY'
						or (:#{#language.name()} = 'DUTCH_ONLY' and (request.language = 'ANY' or request.language = 'DUTCH_ONLY'))
						or (:#{#language.name()} = 'ENGLISH_ONLY' and (request.language = 'ANY' or request.language = 'ENGLISH_ONLY')))
			""")
	//	and (coalesce(:#{#filter.rooms}) is null or request.room in :#{#filter.rooms})
	List<LabRequest> findAllByFilterUpcoming(
			@Param("labs") List<Lab> labs,
			@Param("filter") RequestTableFilterDTO filter,
			@Param("language") Language language);

	/**
	 * Finds all requests that pass the given filter. This filter is a DTO transferred from the page
	 * requesting to see these requests. The filter contains all labs, rooms, assignments, etc. that need to
	 * be displayed. If a field in the filter is left as an empty set, the filter ignores it.
	 *
	 * @param  labs     The labs that the should also be kept in the filter.
	 * @param  filter   The filter to apply to the boolean expression.
	 * @param  language The assistant's language choice
	 * @return          The filtered list of requests.
	 */
	@Query(value = """
				select request from LabRequest request
				where request.session.id in :#{#labs.![id]}
					and (cast(:#{#filter.assigned.isEmpty()} as boolean) or request.eventInfo.assignedTo in :#{#filter.assigned})
					and (cast(:#{#filter.labs.isEmpty()} as boolean) or request.session.id in :#{#filter.labs})
					and (cast(:#{#filter.rooms.isEmpty()} as boolean) or request.room in :#{#filter.rooms})
					and (cast(:#{#filter.onlineModes.isEmpty()} as boolean) or request.onlineMode in :#{#filter.onlineModes.![#this.ordinal()]})
					and (cast(:#{#filter.assignments.isEmpty()} as boolean) or request.assignment in :#{#filter.assignments})
					and (cast(:#{#filter.requestStatuses.isEmpty()} as boolean) or request.eventInfo.status in :#{#filter.requestStatuses.![#this.name()]})
					and (cast(:#{#filter.requestTypes.isEmpty()} as boolean) or request.requestType in :#{#filter.requestTypes.![#this.ordinal()]})
					and (:#{#language.name()} = 'ANY'
						or (:#{#language.name()} = 'DUTCH_ONLY' and (request.language = 'ANY' or request.language = 'DUTCH_ONLY'))
						or (:#{#language.name()} = 'ENGLISH_ONLY' and (request.language = 'ANY' or request.language = 'ENGLISH_ONLY')))
			""")
	List<LabRequest> findAllByFilter(
			@Param("labs") List<Lab> labs,
			@Param("filter") RequestTableFilterDTO filter,
			@Param("language") Language language);
}
