import dayjs from "dayjs"
import { z } from "zod"

import { Lang } from "@/constants/languages"

import { ChildBedMapEnum } from "@/types/components/bookingWidget/enums"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import { Country } from "@/types/enums/country"

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 getHotelsByHotelIdsAvailabilityInputSchema = z
  .object({
    hotelIds: z.array(z.number()),
    roomStayStartDate: z.string().refine(
      (val) => {
        const fromDate = dayjs(val)
        return fromDate.isValid()
      },
      {
        message: "FROMDATE_INVALID",
      }
    ),
    roomStayEndDate: z.string().refine(
      (val) => {
        const toDate = dayjs(val)

        return toDate.isValid()
      },
      {
        message: "TODATE_INVALID",
      }
    ),
    adults: z.number(),
    children: z.string().optional(),
    bookingCode: z.string().optional().default(""),
  })
  .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",
    }
  )

const childrenInRoomSchema = z
  .array(
    z.object({
      age: z.number(),
      bed: z.nativeEnum(ChildBedMapEnum),
    })
  )
  .optional()

const baseRoomSchema = z.object({
  adults: z.number().int().min(1),
  bookingCode: z.string().optional(),
  childrenInRoom: childrenInRoomSchema,
  packages: z
    .array(z.nativeEnum({ ...BreakfastPackageEnum, ...RoomPackageCodeEnum }))
    .optional(),
})

const selectedRoomSchema = z.object({
  counterRateCode: z.string().optional(),
  rateCode: z.string(),
  roomTypeCode: z.string(),
})

const baseBookingSchema = z.object({
  bookingCode: z.string().optional(),
  fromDate: z.string(),
  hotelId: z.string(),
  searchType: z.string().optional(),
  toDate: z.string(),
})

export const selectRateRoomsAvailabilityInputSchema = z
  .object({
    booking: baseBookingSchema.extend({
      rooms: z.array(baseRoomSchema),
    }),
    lang: z.nativeEnum(Lang),
  })
  .refine(
    (data) => {
      const fromDate = dayjs(data.booking.fromDate)

      return fromDate.isValid()
    },
    {
      message: "FROMDATE_INVALID",
    }
  )
  .refine(
    (data) => {
      const toDate = dayjs(data.booking.toDate)

      return toDate.isValid()
    },
    {
      message: "TODATE_INVALID",
    }
  )
  .refine(
    (data) => {
      const fromDate = dayjs(data.booking.fromDate).startOf("day")
      const toDate = dayjs(data.booking.toDate).startOf("day")

      return fromDate.isBefore(toDate)
    },
    {
      message: "TODATE_MUST_BE_AFTER_FROMDATE",
    }
  )
  .refine(
    (data) => {
      const fromDate = dayjs(data.booking.fromDate)
      const today = dayjs().startOf("day")

      return fromDate.isSameOrAfter(today)
    },
    {
      message: "FROMDATE_CANNOT_BE_IN_THE_PAST",
    }
  )

export const selectRateRoomAvailabilityInputSchema = z.object({
  booking: baseBookingSchema.extend({
    room: baseRoomSchema,
  }),
  lang: z.nativeEnum(Lang),
})

export const enterDetailsRoomsAvailabilityInputSchema = z.object({
  booking: baseBookingSchema.extend({
    rooms: z.array(baseRoomSchema.merge(selectedRoomSchema)),
  }),
  lang: z.nativeEnum(Lang),
})

export const myStayRoomAvailabilityInputSchema = z.object({
  booking: baseBookingSchema.extend({
    room: baseRoomSchema.merge(selectedRoomSchema),
  }),
  lang: z.nativeEnum(Lang),
})

export const roomFeaturesInputSchema = z.object({
  adults: z.number(),
  childrenInRoom: childrenInRoomSchema,
  endDate: z.string(),
  hotelId: z.string(),
  lang: z.nativeEnum(Lang),
  roomFeatureCodes: z
    .array(z.nativeEnum({ ...BreakfastPackageEnum, ...RoomPackageCodeEnum }))
    .optional(),
  startDate: z.string(),
})

export type RoomFeaturesInput = z.input<typeof roomFeaturesInputSchema>

export const hotelInputSchema = z.object({
  hotelId: z.string(),
  isCardOnlyPayment: z.boolean(),
  language: z.nativeEnum(Lang),
})

export const getHotelsByCSFilterInput = z.object({
  locationFilter: z
    .object({
      city: z.string().nullable(),
      country: z.nativeEnum(Country).nullable(),
      excluded: z.array(z.string()),
    })
    .nullable(),
  hotelsToInclude: z.array(z.string()),
})
export interface GetHotelsByCSFilterInput
  extends z.infer<typeof getHotelsByCSFilterInput> {}

export const nearbyHotelIdsInput = z.object({
  hotelId: z.string(),
})

export const getDestinationsMapDataInput = z
  .object({
    lang: z.nativeEnum(Lang),
    warmup: z.boolean().optional(),
  })
  .optional()

export const breakfastPackageInputSchema = z.object({
  adults: z.number().min(1, { message: "at least one adult is required" }),
  fromDate: z
    .string()
    .min(1, { message: "fromDate is required" })
    .pipe(z.coerce.date()),
  hotelId: z.string().min(1, { message: "hotelId is required" }),
  toDate: z
    .string()
    .min(1, { message: "toDate is required" })
    .pipe(z.coerce.date()),
})

export const ancillaryPackageInputSchema = z.object({
  fromDate: z
    .string()
    .min(1, { message: "fromDate is required" })
    .pipe(z.coerce.date()),
  hotelId: z.string().min(1, { message: "hotelId is required" }),
  toDate: z.string().pipe(z.coerce.date()).optional(),
})

export const roomPackagesInputSchema = z.object({
  adults: z.number(),
  children: z.number().optional().default(0),
  endDate: z.string(),
  hotelId: z.string(),
  lang: z.nativeEnum(Lang),
  packageCodes: z.array(z.string()).optional().default([]),
  startDate: z.string(),
})
export const cityCoordinatesInputSchema = z.object({
  city: z.string(),
  hotel: z.object({
    address: z.string().optional(),
  }),
})

export const getMeetingRoomsInputSchema = z.object({
  hotelId: z.string(),
  language: z.string(),
})

export const getAdditionalDataInputSchema = z.object({
  hotelId: z.string(),
  language: z.string(),
})

export const getHotelsByCountryInput = z.object({
  country: z.nativeEnum(Country),
})

export const getHotelsByCityIdentifierInput = z.object({
  cityIdentifier: z.string(),
})

export const getLocationsInput = z.object({
  lang: z.nativeEnum(Lang),
})

export const getLocationsUrlsInput = z.object({
  lang: z.nativeEnum(Lang),
})
