import dayjs from "dayjs" import { z } from "zod" import { getCacheClient } from "@scandic-hotels/common/dataCache" import { env } from "../../../../env/server" import { unauthorizedError } from "../../../errors" import { safeProtectedServiceProcedure } from "../../../procedures" import { toApiLang } from "../../../utils" import { getRedemptionTokenSafely } from "../../../utils/getRedemptionTokenSafely" import { getUserPointsBalance } from "../../../utils/getUserPointsBalance" import { getHotelsAvailabilityByCity } from "../services/getHotelsAvailabilityByCity" export type HotelsAvailabilityInputSchema = z.output< typeof hotelsAvailabilityInputSchema > export const hotelsAvailabilityInputSchema = z .object({ cityId: z.string(), roomStayStartDate: z.string().refine( (val) => { const fromDate = dayjs(val) return fromDate.isValid() }, { message: "FROMDATE_INVALID", } ), roomStayEndDate: z.string().refine( (val) => { const fromDate = dayjs(val) return fromDate.isValid() }, { message: "TODATE_INVALID", } ), adults: z.number(), children: z.string().optional(), bookingCode: z.string().optional().default(""), redemption: z.boolean().optional().default(false), }) .refine( (data) => { const fromDate = dayjs(data.roomStayStartDate).startOf("day") const toDate = dayjs(data.roomStayEndDate).startOf("day") return fromDate.isBefore(toDate) }, { message: "FROMDATE_BEFORE_TODATE", } ) .refine( (data) => { const fromDate = dayjs(data.roomStayStartDate) const today = dayjs().startOf("day") return fromDate.isSameOrAfter(today) }, { message: "FROMDATE_CANNOT_BE_IN_THE_PAST", } ) export const hotelsByCity = safeProtectedServiceProcedure .input(hotelsAvailabilityInputSchema) .use(async ({ ctx, input, next }) => { if (input.redemption) { if (ctx.session?.token.access_token) { const pointsValue = await getUserPointsBalance(ctx.session) const token = getRedemptionTokenSafely(ctx.session, ctx.serviceToken) if (pointsValue && token) { return next({ ctx: { token: token, userPoints: pointsValue ?? 0, }, input, }) } } throw unauthorizedError() } return next({ ctx: { token: ctx.serviceToken, }, input, }) }) .query(async ({ ctx, input }) => { const { lang } = ctx const apiLang = toApiLang(lang) const { cityId, roomStayStartDate, roomStayEndDate, adults, children, bookingCode, redemption, } = input // In case of redemption do not cache result if (redemption) { return getHotelsAvailabilityByCity( input, apiLang, ctx.token, ctx.userPoints ) } const cacheClient = await getCacheClient() return await cacheClient.cacheOrGet( `${cityId}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}`, async () => { return getHotelsAvailabilityByCity(input, apiLang, ctx.token) }, env.CACHE_TIME_CITY_SEARCH ) })