import { z } from "zod"

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

import { parseSearchParams, serializeSearchParams } from "./searchParams"

import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
import type { DetailsBooking } from "@/types/components/hotelReservation/enterDetails/details"
import type { SelectHotelBooking } from "@/types/components/hotelReservation/selectHotel/selectHotel"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import type {
  Room,
  SelectRateBooking,
} from "@/types/components/hotelReservation/selectRate/selectRate"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import type { NextSearchParams } from "@/types/params"

export function removeMultipleSlashes(pathname: string) {
  return pathname.replaceAll(/\/\/+/g, "/")
}

export function removeTrailingSlash(pathname: string) {
  if (pathname.endsWith("/")) {
    // Remove the trailing slash
    return pathname.slice(0, -1)
  }
  return pathname
}

type PartialRoom = { rooms?: Partial<Room>[] }

export type SelectHotelParams<T> = Omit<T, "hotel"> & {
  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
const adultsSchema = z.coerce.number().min(1).max(6).catch(0)
const childAgeSchema = z.coerce.number().catch(-1)
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) {
    console.log("[URL] Error parsing search params for booking widget:", error)
    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) {
    console.log("[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) {
    console.log("[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) {
    console.log("[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,
  })
}

/**
 * Returns the TLD (top-level domain) for a given language.
 * @param lang - The language to get the TLD for
 * @returns The TLD for the given language
 */
export function getTldForLanguage(lang: Lang): string {
  switch (lang) {
    case Lang.sv:
      return "se"
    case Lang.no:
      return "no"
    case Lang.da:
      return "dk"
    case Lang.fi:
      return "fi"
    case Lang.de:
      return "de"
    default:
      return "com"
  }
}

/**
 * Constructs a URL with the correct TLD (top-level domain) based on lang, for current web.
 * @param params - Object containing path, lang, and baseUrl
 * @param params.path - The path to append to the URL
 * @param params.lang - The language to use for TLD
 * @param params.baseUrl - The base URL to use (e.g. https://www.scandichotels.com)
 * @returns The complete URL with language-specific TLD
 */
export function getCurrentWebUrl({
  path,
  lang,
  baseUrl = "https://www.scandichotels.com", // Fallback for ephemeral environments (e.g. deploy previews).
}: {
  path: string
  lang: Lang
  baseUrl?: string
}): string {
  const tld = getTldForLanguage(lang)
  const url = new URL(path, baseUrl)

  if (tld !== "com") {
    url.host = url.host.replace(".com", `.${tld}`)
  }

  return url.toString()
}
