"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getVehicleAvailabilitySummary = getVehicleAvailabilitySummary; const prisma_1 = require("../lib/prisma"); const BLOCKING_RESERVATION_STATUSES = ['DRAFT', 'CONFIRMED', 'ACTIVE']; function startOfTodayUtc() { const now = new Date(); return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); } function overlaps(range, source) { return source.startDate < range.endDate && source.endDate > range.startDate; } function getNextAvailableAt(anchor, sources) { const sorted = sources .filter((source) => source.endDate > anchor) .sort((a, b) => { const startDelta = a.startDate.getTime() - b.startDate.getTime(); if (startDelta !== 0) return startDelta; return a.endDate.getTime() - b.endDate.getTime(); }); let candidate = anchor; let advanced = false; for (const source of sorted) { if (source.endDate <= candidate) continue; if (source.startDate > candidate) break; candidate = new Date(Math.max(candidate.getTime(), source.endDate.getTime())); advanced = true; } return advanced ? candidate : null; } async function getVehicleAvailabilitySummary(vehicleId, { range, includeDraftReservations = true, companyId, } = {}) { const vehicle = await prisma_1.prisma.vehicle.findFirstOrThrow({ where: { id: vehicleId, ...(companyId ? { companyId } : {}) }, select: { status: true }, }); if (vehicle.status === 'OUT_OF_SERVICE') { return { available: false, status: 'UNAVAILABLE', nextAvailableAt: null, blockingReason: 'STATUS', }; } const anchor = range?.startDate ?? startOfTodayUtc(); const reservationStatuses = includeDraftReservations ? [...BLOCKING_RESERVATION_STATUSES] : BLOCKING_RESERVATION_STATUSES.filter((status) => status !== 'DRAFT'); const [reservations, blocks] = await Promise.all([ prisma_1.prisma.reservation.findMany({ where: { vehicleId, status: { in: reservationStatuses }, endDate: { gt: anchor }, }, select: { startDate: true, endDate: true }, orderBy: { startDate: 'asc' }, }), prisma_1.prisma.vehicleCalendarBlock.findMany({ where: { vehicleId, endDate: { gt: anchor }, }, select: { startDate: true, endDate: true, type: true }, orderBy: { startDate: 'asc' }, }), ]); const sources = [ ...reservations.map((reservation) => ({ startDate: reservation.startDate, endDate: reservation.endDate, kind: 'RESERVATION', })), ...blocks.map((block) => ({ startDate: block.startDate, endDate: block.endDate, kind: block.type === 'MAINTENANCE' ? 'MAINTENANCE' : 'BLOCK', })), ]; const overlappingSources = range ? sources.filter((source) => overlaps(range, source)) : sources.filter((source) => source.startDate <= anchor && source.endDate > anchor); const currentSource = overlappingSources.sort((a, b) => { const priority = { MAINTENANCE: 0, BLOCK: 1, RESERVATION: 2 }; return priority[a.kind] - priority[b.kind]; })[0]; if (vehicle.status === 'MAINTENANCE') { return { available: false, status: 'MAINTENANCE', nextAvailableAt: getNextAvailableAt(anchor, sources) ?? null, blockingReason: currentSource?.kind ?? 'STATUS', }; } if (!currentSource) { return { available: true, status: 'AVAILABLE', nextAvailableAt: null, blockingReason: null, }; } const nextAvailableAnchor = currentSource.startDate > anchor ? currentSource.startDate : anchor; return { available: false, status: currentSource.kind === 'RESERVATION' ? 'RESERVED' : currentSource.kind === 'MAINTENANCE' ? 'MAINTENANCE' : 'UNAVAILABLE', nextAvailableAt: getNextAvailableAt(nextAvailableAnchor, sources), blockingReason: currentSource.kind, }; } //# sourceMappingURL=vehicleAvailabilityService.js.map