"use client"
import deepmerge from "deepmerge"
import { useEffect, useRef, useState } from "react"

import { isSameBooking } from "@scandic-hotels/booking-flow/utils/isSameBooking"
import { dt } from "@scandic-hotels/common/dt"

import { createDetailsStore } from "@/stores/enter-details"
import {
  clearSessionStorage,
  getTotalPrice,
  readFromSessionStorage,
  writeToSessionStorage,
} from "@/stores/enter-details/helpers"

import { getMultiroomDetailsSchema } from "@/components/HotelReservation/EnterDetails/Details/Multiroom/schema"
import { guestDetailsSchema } from "@/components/HotelReservation/EnterDetails/Details/RoomOne/schema"
import LoadingSpinner from "@/components/LoadingSpinner"
import { DetailsContext } from "@/contexts/Details"

import type { DetailsStore } from "@/types/contexts/enter-details"
import { StepEnum } from "@/types/enums/step"
import type { DetailsProviderProps } from "@/types/providers/enter-details"
import type { InitialState, RoomState } from "@/types/stores/enter-details"

export default function EnterDetailsProvider({
  booking,
  breakfastPackages,
  children,
  lang,
  rooms,
  searchParamsStr,
  user,
  vat,
  roomCategories,
}: DetailsProviderProps) {
  // This state is needed to be able to use defaultValues for
  // react-hook-form since values needs to be there on mount
  // and since we read from SessionStorage we need to delay
  // rendering the form until that has been done.
  const [hasInitializedStore, setHasInitializedStore] = useState(false)
  const storeRef = useRef<DetailsStore>(undefined)
  if (!storeRef.current) {
    const initialData: InitialState = {
      booking,
      rooms: rooms
        .filter((r) => r.bedTypes?.length) // TODO: how to handle room without bedtypes?
        .map((room) => ({
          isAvailable: room.isAvailable,
          breakfastIncluded: room.breakfastIncluded,
          cancellationText: room.cancellationText,
          cancellationRule: room.cancellationRule,
          rateDetails: room.rateDetails,
          memberRateDetails: room.memberRateDetails,
          rateTitle: room.rateTitle,
          roomFeatures: room.packages,
          roomRate: room.roomRate,
          roomType: room.roomType,
          roomTypeCode: room.roomTypeCode,
          bedTypes: room.bedTypes,
          bedType:
            room.bedTypes?.length === 1
              ? {
                  roomTypeCode: room.bedTypes[0].value,
                  description: room.bedTypes[0].description,
                  type: room.bedTypes[0].type,
                }
              : undefined,
          mustBeGuaranteed: room.mustBeGuaranteed,
          memberMustBeGuaranteed: room.memberMustBeGuaranteed,
          isFlexRate: room.isFlexRate,
        })),
      vat,
      roomCategories,
    }

    storeRef.current = createDetailsStore(
      initialData,
      searchParamsStr,
      user,
      breakfastPackages,
      lang
    )
  }

  useEffect(() => {
    const storedValues = readFromSessionStorage()
    if (!storedValues) {
      setHasInitializedStore(true)
      return
    }

    if (!isSameBooking(storedValues.booking, booking)) {
      clearSessionStorage()
      setHasInitializedStore(true)
      return
    }

    const store = storeRef.current?.getState()
    if (!store) {
      setHasInitializedStore(true)
      return
    }

    const updatedRooms = storedValues.rooms.map((storedRoom, idx) => {
      const room = store.rooms[idx]
      if (!room) {
        return null
      }
      // Need to create a deep new copy
      // since store is readonly
      const currentRoom = deepmerge({}, room)

      if (!currentRoom.room.isAvailable) {
        return currentRoom
      }

      if (!currentRoom.room.bedType && storedRoom.room.bedType) {
        const sameBed = currentRoom.room.bedTypes.find(
          (bedType) => bedType.value === storedRoom.room.bedType?.roomTypeCode
        )
        if (sameBed) {
          currentRoom.room.bedType = {
            description: sameBed.description,
            roomTypeCode: sameBed.value,
            type: sameBed.type,
          }
          currentRoom.steps[StepEnum.selectBed].isValid = true
        }
      }

      if (
        currentRoom.steps[StepEnum.breakfast] &&
        currentRoom.room.breakfast === undefined &&
        (storedRoom.room.breakfast || storedRoom.room.breakfast === false)
      ) {
        currentRoom.room.breakfast = storedRoom.room.breakfast
        currentRoom.steps[StepEnum.breakfast].isValid = true
      }

      // User is already added for main room
      if (!user || (user && idx > 0)) {
        currentRoom.room.guest = deepmerge(
          currentRoom.room.guest,
          storedRoom.room.guest
        )
      }
      if (
        !currentRoom.room.specialRequest.comment &&
        storedRoom.room.specialRequest.comment
      ) {
        currentRoom.room.specialRequest.comment =
          storedRoom.room.specialRequest.comment
      }
      const validGuest =
        idx > 0
          ? getMultiroomDetailsSchema().safeParse(currentRoom.room.guest)
          : guestDetailsSchema.safeParse(currentRoom.room.guest)
      if (validGuest.success) {
        currentRoom.steps[StepEnum.details].isValid = true
      }

      const invalidStep = Object.values(currentRoom.steps).find(
        (step) => !step.isValid
      )

      currentRoom.isComplete = !invalidStep

      return currentRoom
    })

    const canProceedToPayment = updatedRooms.every(
      (room) => room?.isComplete && room?.room.isAvailable
    )

    const filteredOutMissingRooms = updatedRooms.filter(
      (room): room is RoomState => !!room
    )

    const nights = dt(booking.toDate).diff(booking.fromDate, "days")

    const totalPrice = getTotalPrice(
      filteredOutMissingRooms.map((r) => r.room),
      !!user,
      nights
    )

    // Need to create a deep new copy since store is readonly
    const availableBeds = deepmerge({}, store.availableBeds)
    for (const filteredOutMissingRoom of filteredOutMissingRooms) {
      if (filteredOutMissingRoom.room.bedType) {
        const roomTypeCode = filteredOutMissingRoom.room.bedType.roomTypeCode
        availableBeds[roomTypeCode] = Math.max(
          availableBeds[roomTypeCode] - 1,
          0
        )
      }
    }

    writeToSessionStorage({
      booking,
      rooms: filteredOutMissingRooms,
    })

    storeRef.current?.setState({
      availableBeds,
      canProceedToPayment,
      rooms: filteredOutMissingRooms,
      totalPrice,
    })

    setHasInitializedStore(true)
  }, [booking, rooms, user])

  return (
    <DetailsContext.Provider value={storeRef.current}>
      {hasInitializedStore ? children : <LoadingSpinner fullPage />}
    </DetailsContext.Provider>
  )
}
