"use client"
import { notFound } from "next/navigation"
import { use, useRef } from "react"
import { useIntl } from "react-intl"

import { dt } from "@scandic-hotels/common/dt"
import { trpc } from "@scandic-hotels/trpc/client"

import { createMyStayStore } from "@/stores/my-stay"

import { MyStaySkeleton } from "@/components/HotelReservation/MyStay/myStaySkeleton"
import { MyStayContext } from "@/contexts/MyStay"

import type { Lang } from "@scandic-hotels/common/constants/language"
import type {
  BookingConfirmation,
  BookingConfirmationSchema,
} from "@scandic-hotels/trpc/types/bookingConfirmation"
import type { RoomCategories } from "@scandic-hotels/trpc/types/hotel"
import type { CreditCard } from "@scandic-hotels/trpc/types/user"

import type { Packages } from "@/types/components/myPages/myStay/ancillaries"
import type { MyStayStore } from "@/types/contexts/my-stay"

interface MyStayProviderProps {
  bookingConfirmation: BookingConfirmation
  breakfastPackages: Packages | null
  isLoggedIn?: boolean
  lang: Lang
  linkedReservationsPromise: Promise<BookingConfirmationSchema[]>
  refId: string
  roomCategories: RoomCategories
  savedCreditCards: CreditCard[] | null
}

export default function MyStayProvider({
  bookingConfirmation,
  breakfastPackages,
  children,
  lang,
  linkedReservationsPromise,
  refId,
  roomCategories,
  savedCreditCards,
  isLoggedIn,
}: React.PropsWithChildren<MyStayProviderProps>) {
  const storeRef = useRef<MyStayStore>(undefined)
  const intl = useIntl()

  const { data, error, isFetching, isFetchedAfterMount } =
    trpc.booking.get.useQuery(
      {
        refId: bookingConfirmation.booking.refId,
        lang,
      },
      {
        initialData: bookingConfirmation,
        refetchOnMount: false,
        refetchOnWindowFocus: false,
      }
    )

  // We need this two-step business since `use` must resolve
  // the promise passed from the server whereas `useQuery`
  // needs to own the data when in the client so that invalidations
  // actually triggers a refetch of the data
  const linkedReservationsResponses = use(linkedReservationsPromise)
  const {
    data: linkedReservations,
    error: linkedReservationsError,
    isFetching: linkedReservationsIsFetching,
    isFetchedAfterMount: linkedReservationsIsFetchedAfterMount,
  } = trpc.booking.linkedReservations.useQuery(
    {
      lang,
      refId: bookingConfirmation.booking.refId,
    },
    {
      initialData: linkedReservationsResponses,
      refetchOnMount: false,
      refetchOnWindowFocus: false,
    }
  )

  if (isFetching || linkedReservationsIsFetching) {
    return <MyStaySkeleton />
  }

  if (!data || error || linkedReservationsError) {
    return notFound()
  }

  const rooms = [data.booking, ...linkedReservations]

  // Following the API logic for determining if booking is in the past.
  // This is the same logic as used for separating stays in /future and /past endpoints on the API
  const now = dt()
  const isPastBooking = dt(data.booking.checkOutDate).isBefore(now, "day")

  const hasInvalidatedQueryAndRefetched =
    (isFetchedAfterMount && data) ||
    (linkedReservationsIsFetchedAfterMount && linkedReservations)

  // eslint-disable-next-line react-hooks/refs
  if (!storeRef.current || hasInvalidatedQueryAndRefetched) {
    storeRef.current = createMyStayStore({
      breakfastPackages,
      hotel: bookingConfirmation.hotel,
      intl,
      refId,
      roomCategories,
      rooms,
      savedCreditCards,
      isLoggedIn,
      isPastBooking,
    })
  }

  return (
    // eslint-disable-next-line react-hooks/refs
    <MyStayContext.Provider value={storeRef.current}>
      {children}
    </MyStayContext.Provider>
  )
}
