import { differenceInCalendarDays, format, isWeekend } from "date-fns"

import { CancellationRuleEnum } from "@/constants/booking"

import { getSpecialRoomType } from "@/utils/specialRoomType"

import { invertedBedTypeMap } from "../../utils"

import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import {
  TrackingChannelEnum,
  type TrackingSDKAncillaries,
  type TrackingSDKHotelInfo,
  type TrackingSDKPageData,
  type TrackingSDKPaymentInfo,
} from "@/types/components/tracking"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import { CurrencyEnum } from "@/types/enums/currency"
import { RateEnum } from "@/types/enums/rate"
import type { Room } from "@/types/stores/booking-confirmation"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
import type { RateDefinition } from "@/types/trpc/routers/hotel/roomAvailability"
import type { Lang } from "@/constants/languages"

function getRate(cancellationRule: RateDefinition["cancellationRule"] | null) {
  switch (cancellationRule) {
    case "CancellableBefore6PM":
      return RateEnum.flex
    case "Changeable":
      return RateEnum.change
    case "NotCancellable":
      return RateEnum.save
    default:
      return ""
  }
}

function mapAncillaryPackage(
  ancillaryPackage: BookingConfirmation["booking"]["packages"][number],
  operaId: string
) {
  const isPoints = ancillaryPackage.currency === CurrencyEnum.POINTS
  return {
    hotelid: operaId,
    productCategory: "", // TODO: Add category
    productId: ancillaryPackage.code,
    productName: ancillaryPackage.description,
    productPoints: isPoints ? ancillaryPackage.totalPrice : 0,
    productPrice: isPoints ? 0 : ancillaryPackage.totalPrice,
    productType:
      ancillaryPackage.code === BreakfastPackageEnum.REGULAR_BREAKFAST
        ? "food"
        : "room preference",
    productUnits: ancillaryPackage.totalUnit,
    productDeliveryTime: "",
  }
}

export function getTracking(
  lang: Lang,
  booking: BookingConfirmation["booking"],
  hotel: BookingConfirmation["hotel"],
  rooms: Room[],
  searchTerm?: string
) {
  const arrivalDate = new Date(booking.checkInDate)
  const departureDate = new Date(booking.checkOutDate)

  const pageTrackingData: TrackingSDKPageData = {
    channel: TrackingChannelEnum.hotelreservation,
    domainLanguage: lang,
    pageId: "booking-confirmation",
    pageName: `hotelreservation|confirmation`,
    pageType: "confirmation",
    siteSections: `hotelreservation|confirmation`,
    siteVersion: "new-web",
  }

  const noOfAdults = rooms.map((r) => r.adults).join(",")
  const noOfChildren = rooms.map((r) => r.childrenAges?.length ?? 0).join(",")
  const noOfRooms = rooms.length
  const isFlexBooking =
    booking.rateDefinition.cancellationRule ===
    CancellationRuleEnum.CancellableBefore6PM

  const ancillaries: TrackingSDKAncillaries = rooms
    .flatMap((r) => r.packages)
    .filter(
      (p) =>
        p.code === RoomPackageCodeEnum.PET_ROOM ||
        p.code === BreakfastPackageEnum.REGULAR_BREAKFAST
    )
    .map((pkg) => mapAncillaryPackage(pkg, hotel.operaId))

  const hotelsTrackingData: TrackingSDKHotelInfo = {
    ageOfChildren: rooms.map((r) => r.childrenAges?.join(",") ?? "").join("|"),
    analyticsRateCode: rooms
      .map((r) => getRate(r.rateDefinition.cancellationRule))
      .join("|"),
    arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
    bedType: rooms
      .map((r) => r.bedDescription)
      .join(",")
      .toLowerCase(),
    bnr: rooms.map((r) => r.confirmationNumber).join(","),
    bookingCode: rooms.map((room) => room.bookingCode ?? "n/a").join(", "),
    bookingCodeAvailability: booking.bookingCode
      ? rooms.map((room) => (room.bookingCode ? "true" : "false")).join(", ")
      : undefined,
    bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
    breakfastOption: rooms
      .map((r) => {
        if (r.breakfastIncluded || r.breakfast) {
          return "breakfast buffet"
        }
        return "no breakfast"
      })
      .join(","),
    childBedPreference: rooms
      .map(
        (r) =>
          r.childBedPreferences
            .map((cbp) =>
              Array(cbp.quantity).fill(invertedBedTypeMap[cbp.bedType])
            )
            .join(",") ?? ""
      )
      .join("|"),
    country: hotel?.address.country,
    departureDate: format(departureDate, "yyyy-MM-dd"),
    duration: differenceInCalendarDays(departureDate, arrivalDate),
    hotelID: hotel.operaId,
    leadTime: differenceInCalendarDays(arrivalDate, new Date()),
    noOfAdults,
    noOfChildren,
    noOfRooms,
    rateCode: rooms.map((r) => r.rateDefinition.rateCode).join(","),
    rateCodeCancellationRule: rooms
      .map((r) => r.rateDefinition.cancellationText)
      .join(",")
      .toLowerCase(),
    rateCodeName: rooms
      .map((r) => r.rateDefinition.title)
      .join(",")
      .toLowerCase(),
    //rateCodeType: , //TODO: Add when available in API. "regular, promotion, corporate etx",
    region: hotel?.address.city,
    revenueCurrencyCode: [...new Set(rooms.map((r) => r.currencyCode))].join(
      ","
    ),
    rewardNight: booking.roomPoints > 0 ? "yes" : "no",
    rewardNightAvailability: booking.roomPoints > 0 ? "true" : "false",
    points: booking.roomPoints > 0 ? booking.roomPoints : undefined,
    roomPrice: rooms.reduce((total, room) => total + room.roomPrice, 0),
    roomTypeCode: rooms.map((r) => r.roomTypeCode ?? "").join(","),
    searchTerm,
    searchType: "hotel",
    specialRoomType: rooms
      .map((room) => getSpecialRoomType(room.packages))
      .join(","),
    totalPrice: rooms.reduce((total, room) => total + room.totalPrice, 0),
    lateArrivalGuarantee: booking.rateDefinition.mustBeGuaranteed
      ? "mandatory"
      : isFlexBooking
        ? booking.guaranteeInfo
          ? "yes"
          : "no"
        : "na",
    guaranteedProduct: booking.guaranteeInfo && isFlexBooking ? "room" : "na",
  }

  const paymentInfo: TrackingSDKPaymentInfo = {
    paymentStatus: "confirmed",
    status:
      booking.guaranteeInfo && isFlexBooking
        ? "glacardsaveconfirmed"
        : undefined,
    type:
      booking.guaranteeInfo && isFlexBooking
        ? booking.guaranteeInfo.cardType
        : undefined,
  }

  return {
    hotelsTrackingData,
    pageTrackingData,
    paymentInfo,
    ancillaries,
  }
}
