import { z } from "zod" import { logger } from "@scandic-hotels/common/logger" import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast" import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter" import { type BookingWidgetSearchData } from "../components/BookingWidget" import { type BookingSearchType, bookingSearchTypes } from "../misc/searchType" import { parseSearchParams, serializeSearchParams } from "./searchParams" import type { Child } from "@scandic-hotels/trpc/types/child" import type { PackageEnum } from "@scandic-hotels/trpc/types/packages" import type { NextSearchParams } from "../types" import type { SelectRateBooking } from "../types/components/selectRate/selectRate" type PartialRoom = { rooms?: Partial[] } export type SelectHotelParams = Omit & { hotelId: string } & PartialRoom export function searchParamsToRecord(searchParams: URLSearchParams) { return Object.fromEntries(searchParams.entries()) } const keyRenameMap = { room: "rooms", ratecode: "rateCode", counterratecode: "counterRateCode", roomtype: "roomTypeCode", fromdate: "fromDate", todate: "toDate", hotel: "hotelId", child: "childrenInRoom", searchtype: "searchType", } const typeHints = { filters: "COMMA_SEPARATED_ARRAY", packages: "COMMA_SEPARATED_ARRAY", } as const export const adultsSchema = z.coerce.number().min(1).max(6).catch(0) export const childAgeSchema = z.coerce.number().catch(-1) export const childBedSchema = z.coerce.number().catch(-1) const searchTypeSchema = z.enum(bookingSearchTypes).optional().catch(undefined) export function parseBookingWidgetSearchParams( searchParams: NextSearchParams ): BookingWidgetSearchData { try { const result = parseSearchParams(searchParams, { keyRenameMap, typeHints, schema: z.object({ city: z.string().optional(), hotelId: z.string().optional(), fromDate: z.string().optional(), toDate: z.string().optional(), bookingCode: z.string().optional(), searchType: searchTypeSchema, rooms: z .array( z.object({ adults: adultsSchema, childrenInRoom: z .array( z.object({ bed: childBedSchema, age: childAgeSchema, }) ) .optional() .default([]), }) ) .optional(), }), }) return result } catch (error) { logger.error( "[URL] Error parsing search params for booking widget:", error, searchParams ) return {} } } export function parseSelectHotelSearchParams( searchParams: NextSearchParams ): SelectHotelBooking | null { try { const result = parseSearchParams(searchParams, { keyRenameMap, typeHints, schema: z.object({ city: z.string().optional(), hotelId: z.string().optional(), fromDate: z.string(), toDate: z.string(), bookingCode: z.string().optional(), searchType: searchTypeSchema, rooms: z.array( z.object({ adults: adultsSchema, childrenInRoom: z .array( z.object({ bed: childBedSchema, age: childAgeSchema, }) ) .optional(), }) ), }), }) return result } catch (error) { logger.error("[URL] Error parsing search params for select hotel:", error) return null } } export function parseSelectRateSearchParams( searchParams: NextSearchParams ): SelectRateBooking | null { try { const result = parseSearchParams(searchParams, { keyRenameMap, typeHints, schema: z.object({ city: z.string().optional(), hotelId: z.string(), fromDate: z.string(), toDate: z.string(), searchType: searchTypeSchema, bookingCode: z.string().optional(), rooms: z.array( z.object({ adults: adultsSchema, bookingCode: z.string().optional(), counterRateCode: z.string().optional(), rateCode: z.string().optional(), roomTypeCode: z.string().optional(), packages: z .array( z.nativeEnum({ ...BreakfastPackageEnum, ...RoomPackageCodeEnum, }) ) .optional(), childrenInRoom: z .array( z.object({ bed: childBedSchema, age: childAgeSchema, }) ) .optional(), }) ), }), }) return result } catch (error) { logger.error("[URL] Error parsing search params for select rate:", error) return null } } export function parseDetailsSearchParams( searchParams: NextSearchParams ): DetailsBooking | null { const packageEnum = { ...BreakfastPackageEnum, ...RoomPackageCodeEnum, } as const try { const result = parseSearchParams(searchParams, { keyRenameMap, typeHints, schema: z.object({ city: z.string().optional(), hotelId: z.string(), fromDate: z.string(), toDate: z.string(), searchType: searchTypeSchema, bookingCode: z.string().optional(), rooms: z.array( z.object({ adults: adultsSchema, bookingCode: z.string().optional(), counterRateCode: z.string().optional(), rateCode: z.string(), roomTypeCode: z.string(), packages: z.array(z.nativeEnum(packageEnum)).optional(), childrenInRoom: z .array( z.object({ bed: childBedSchema, age: childAgeSchema, }) ) .optional(), }) ), }), }) return result } catch (error) { logger.error("[URL] Error parsing search params for details:", error) return null } } const reversedKeyRenameMap = Object.fromEntries( Object.entries(keyRenameMap).map(([key, value]) => [value, key]) ) export function serializeBookingSearchParams( obj: | BookingWidgetSearchData | SelectHotelBooking | SelectRateBooking | DetailsBooking, { initialSearchParams }: { initialSearchParams?: URLSearchParams } = {} ) { return serializeSearchParams(obj, { keyRenameMap: reversedKeyRenameMap, initialSearchParams, typeHints, }) } export type DetailsBooking = { hotelId: string fromDate: string toDate: string city?: string bookingCode?: string searchType?: BookingSearchType rooms: { adults: number rateCode: string roomTypeCode: string bookingCode?: string childrenInRoom?: Child[] counterRateCode?: string packages?: PackageEnum[] }[] } export type SelectHotelBooking = { hotelId?: string city?: string fromDate: string toDate: string rooms: { adults: number childrenInRoom?: Child[] }[] bookingCode?: string searchType?: BookingSearchType } export interface Room { adults: number childrenInRoom?: Child[] bookingCode?: string | null counterRateCode?: string | null packages?: PackageEnum[] | null rateCode?: string | null roomTypeCode?: string | null }