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

import { dt } from "@/lib/dt"
import { createDetailsStore } from "@/stores/enter-details"
import {
  calcTotalPrice,
  checkIsSameBooking as checkIsSameBooking,
  clearSessionStorage,
  readFromSessionStorage,
  writeToSessionStorage,
} from "@/stores/enter-details/helpers"

import { multiroomDetailsSchema } from "@/components/HotelReservation/EnterDetails/Details/Multiroom/schema"
import { guestDetailsSchema } from "@/components/HotelReservation/EnterDetails/Details/RoomOne/schema"
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 } from "@/types/stores/enter-details"

export default function EnterDetailsProvider({
  booking,
  breakfastPackages,
  children,
  rooms,
  searchParamsStr,
  user,
  vat,
}: DetailsProviderProps) {
  const storeRef = useRef<DetailsStore>()
  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,
          rateDetails: room.rateDetails,
          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,
                }
              : undefined,
          mustBeGuaranteed: room.mustBeGuaranteed,
          isFlexRate: room.isFlexRate,
        })),
      vat,
    }

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

  useEffect(() => {
    const storedValues = readFromSessionStorage()
    if (!storedValues) {
      return
    }
    const isSameBooking = checkIsSameBooking(storedValues.booking, booking)
    if (!isSameBooking) {
      clearSessionStorage()
      return
    }

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

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

      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,
          }
          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
        )
      }

      const validGuest =
        idx > 0
          ? multiroomDetailsSchema.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
      currentRoom.currentStep = invalidStep ? invalidStep.step : null

      return currentRoom
    })

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

    const nights = dt(booking.toDate).diff(booking.fromDate, "days")
    const currency = (updatedRooms[0].room.roomRate.publicRate?.localPrice
      .currency ||
      updatedRooms[0].room.roomRate.memberRate?.localPrice.currency)!
    const totalPrice = calcTotalPrice(updatedRooms, currency, !!user, nights)

    const activeRoom = updatedRooms.findIndex((room) => !room.isComplete)

    writeToSessionStorage({
      activeRoom,
      booking,
      rooms: updatedRooms,
    })

    storeRef.current?.setState({
      activeRoom: storedValues.activeRoom,
      canProceedToPayment,
      rooms: updatedRooms,
      totalPrice,
    })
  }, [booking, rooms, user])

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