import { differenceInCalendarDays, format, isWeekend } from "date-fns"
import { notFound } from "next/navigation"
import { Suspense } from "react"

import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests"
import { serverClient } from "@/lib/trpc/server"

import HotelDetails from "@/components/HotelReservation/BookingConfirmation/HotelDetails"
import PaymentDetails from "@/components/HotelReservation/BookingConfirmation/PaymentDetails"
import Promos from "@/components/HotelReservation/BookingConfirmation/Promos"
import Receipt from "@/components/HotelReservation/BookingConfirmation/Receipt"
import Rooms from "@/components/HotelReservation/BookingConfirmation/Rooms"
import SidePanel from "@/components/HotelReservation/SidePanel"
import Divider from "@/components/TempDesignSystem/Divider"
import TrackingSDK from "@/components/TrackingSDK"
import { getLang } from "@/i18n/serverContext"

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

import styles from "./bookingConfirmation.module.css"

import type { BookingConfirmationProps } from "@/types/components/hotelReservation/bookingConfirmation/bookingConfirmation"
import {
  TrackingChannelEnum,
  type TrackingSDKHotelInfo,
  type TrackingSDKPageData,
  type TrackingSDKPaymentInfo,
} from "@/types/components/tracking"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"

export default async function BookingConfirmation({
  confirmationNumber,
}: BookingConfirmationProps) {
  const lang = getLang()
  const bookingConfirmation = await getBookingConfirmation(confirmationNumber)

  if (!bookingConfirmation) {
    return notFound()
  }
  const { booking, hotel, room } = bookingConfirmation
  if (!room) {
    return notFound()
  }

  const arrivalDate = new Date(booking.checkInDate)
  const departureDate = new Date(booking.checkOutDate)

  const breakfastPkgSelected = booking.packages.find(
    (pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
  )

  const breakfastAncillary = breakfastPkgSelected && {
    hotelid: hotel.operaId,
    productName: "BreakfastAdult",
    productCategory: "", // TODO: Add category
    productId: breakfastPkgSelected.code ?? "",
    productPrice: +breakfastPkgSelected.unitPrice,
    productUnits: booking.adults,
    productPoints: 0,
    productType: "food",
  }

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

  const initialHotelsTrackingData: TrackingSDKHotelInfo = {
    arrivalDate: format(arrivalDate, "yyyy-MM-dd"),
    departureDate: format(departureDate, "yyyy-MM-dd"),
    noOfAdults: booking.adults,
    noOfChildren: booking.childrenAges?.length,
    ageOfChildren: booking.childrenAges?.join(","),
    childBedPreference: booking?.childBedPreferences
      ?.flatMap((c) => Array(c.quantity).fill(invertedBedTypeMap[c.bedType]))
      .join("|"),
    noOfRooms: 1, // // TODO: Handle multiple rooms
    duration: differenceInCalendarDays(departureDate, arrivalDate),
    leadTime: differenceInCalendarDays(arrivalDate, new Date()),
    searchType: "hotel",
    bookingTypeofDay: isWeekend(arrivalDate) ? "weekend" : "weekday",
    country: hotel?.address.country,
    hotelID: hotel.operaId,
    region: hotel?.address.city,
    rateCode: booking.rateDefinition.rateCode ?? undefined,
    //rateCodeType: , //TODO: Add when available in API. "regular, promotion, corporate etx",
    rateCodeName: booking.rateDefinition.title ?? undefined,
    rateCodeCancellationRule:
      booking.rateDefinition?.cancellationText ?? undefined,
    revenueCurrencyCode: booking.currencyCode,
    breakfastOption: booking.rateDefinition.breakfastIncluded
      ? "breakfast buffet"
      : "no breakfast",
    totalPrice: booking.totalPrice,
    //specialRoomType: getSpecialRoomType(booking.packages), TODO: Add
    //roomTypeName: booking.roomTypeCode ?? undefined, TODO: Do we get the name?
    bedType: room?.bedType.name,
    roomTypeCode: booking.roomTypeCode ?? undefined,
    roomPrice: booking.roomPrice,
    bnr: booking.confirmationNumber ?? undefined,
    ancillaries: breakfastAncillary ? [breakfastAncillary] : [],
  }

  const paymentInfo: TrackingSDKPaymentInfo = {
    paymentStatus: "confirmed",
  }

  const linkedReservations = await Promise.all(
    //  TODO: How to handle partial failure (e.g. one booking can't be fetched)? Need UX/UI
    booking.linkedReservations.map(async (res) => {
      const confirmation = await serverClient().booking.confirmation({
        confirmationNumber: res.confirmationNumber,
      })
      return confirmation
    })
  )

  return (
    <>
      <Confirmation booking={booking} hotel={hotel} room={room}>
        <div className={styles.booking}>
          <Alerts booking={booking} />
          <Rooms
            booking={booking}
            mainRoom={room}
            linkedReservations={booking.linkedReservations}
          />
          <Suspense fallback={null}>
            <PaymentDetails
              booking={booking}
              linkedReservations={linkedReservations}
            />
          </Suspense>
          <Divider color="primaryLightSubtle" />
          <HotelDetails hotel={hotel} />
          <Promos
            confirmationNumber={booking.confirmationNumber}
            hotelId={hotel.operaId}
            lastName={booking.guest.lastName}
          />
          <div className={styles.mobileReceipt}>
            <Suspense fallback={null}>
              <Receipt booking={booking} hotel={hotel} room={room} />
            </Suspense>
          </div>
        </div>
        <aside className={styles.aside}>
          <SidePanel variant="receipt">
            <Suspense fallback={null}>
              <Receipt booking={booking} hotel={hotel} room={room} />
            </Suspense>
          </SidePanel>
        </aside>
      </Confirmation>
      <Suspense fallback={null}>
        <TrackingSDK
          pageData={initialPageTrackingData}
          hotelInfo={initialHotelsTrackingData}
          paymentInfo={paymentInfo}
        />
      </Suspense>
    </>
  )
}
