"use client"

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

import { type RouterOutput, trpc } from "@/lib/trpc/client"
import { createMyStayStore } from "@/stores/my-stay"

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

import type { Packages } from "@/types/components/myPages/myStay/ancillaries"
import type { MyStayStore } from "@/types/contexts/my-stay"
import type { RoomCategories } from "@/types/hotel"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
import type { CreditCard } from "@/types/user"
import type { Lang } from "@/constants/languages"

interface MyStayProviderProps {
  bookingConfirmation: BookingConfirmation
  breakfastPackages: Packages | null
  lang: Lang
  linkedReservationsPromise: Promise<
    RouterOutput["booking"]["linkedReservations"]
  >
  refId: string
  roomCategories: RoomCategories
  savedCreditCards: CreditCard[] | null
}

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

  const storeRef = useRef<MyStayStore>()

  const { data, error, isFetching, isFetchedAfterMount } =
    trpc.booking.confirmation.useQuery(
      {
        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,
    },
    {
      initialData: linkedReservationsResponses,
      refetchOnMount: false,
      refetchOnWindowFocus: false,
    }
  )

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

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

  const rooms = [data.booking].concat(linkedReservations ?? [])

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

  if (!storeRef.current || hasInvalidatedQueryAndRefetched) {
    storeRef.current = createMyStayStore({
      breakfastPackages,
      hotel: bookingConfirmation.hotelData.hotel,
      intl,
      refId,
      roomCategories,
      rooms,
      savedCreditCards,
    })
  }

  return (
    <MyStayContext.Provider value={storeRef.current}>
      {children}
    </MyStayContext.Provider>
  )
}
