import { produce } from "immer"
import { ReadonlyURLSearchParams } from "next/navigation"
import { createContext, useContext } from "react"
import { create, useStore } from "zustand"

import { bedTypeSchema } from "@/components/HotelReservation/EnterDetails/BedType/schema"
import { breakfastStoreSchema } from "@/components/HotelReservation/EnterDetails/Breakfast/schema"
import {
  guestDetailsSchema,
  signedInDetailsSchema,
} from "@/components/HotelReservation/EnterDetails/Details/schema"
import {
  createSelectRateUrl,
  getQueryParamsForEnterDetails,
} from "@/components/HotelReservation/SelectRate/RoomSelection/utils"

import type { BedTypeSchema } from "@/types/components/hotelReservation/enterDetails/bedType"
import { BookingData } from "@/types/components/hotelReservation/enterDetails/bookingData"
import { BreakfastPackage } from "@/types/components/hotelReservation/enterDetails/breakfast"
import { DetailsSchema } from "@/types/components/hotelReservation/enterDetails/details"
import { StepEnum } from "@/types/components/hotelReservation/enterDetails/step"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"

const SESSION_STORAGE_KEY = "enterDetails"

type TotalPrice = {
  local: { price: number; currency: string }
  euro: { price: number; currency: string }
}

export interface EnterDetailsState {
  userData: {
    bedType: BedTypeSchema | undefined
    breakfast: BreakfastPackage | BreakfastPackageEnum.NO_BREAKFAST | undefined
  } & DetailsSchema
  roomData: BookingData
  steps: StepEnum[]
  selectRateUrl: string
  currentStep: StepEnum
  totalPrice: TotalPrice
  isSubmittingDisabled: boolean
  isSummaryOpen: boolean
  isValid: Record<StepEnum, boolean>
  completeStep: (updatedData: Partial<EnterDetailsState["userData"]>) => void
  navigate: (
    step: StepEnum,
    updatedData?: Record<
      string,
      string | boolean | number | BreakfastPackage | BedTypeSchema
    >
  ) => void
  setCurrentStep: (step: StepEnum) => void
  toggleSummaryOpen: () => void
  setTotalPrice: (totalPrice: TotalPrice) => void
  setIsSubmittingDisabled: (isSubmittingDisabled: boolean) => void
}

export function initEditDetailsState(
  currentStep: StepEnum,
  searchParams: ReadonlyURLSearchParams,
  isMember: boolean
) {
  const isBrowser = typeof window !== "undefined"
  const sessionData = isBrowser
    ? sessionStorage.getItem(SESSION_STORAGE_KEY)
    : null

  let roomData: BookingData
  let selectRateUrl: string
  if (searchParams?.size) {
    const data = getQueryParamsForEnterDetails(searchParams)
    roomData = data
    selectRateUrl = `select-rate?${createSelectRateUrl(data)}`
  }

  const defaultUserData: EnterDetailsState["userData"] = {
    bedType: undefined,
    breakfast: undefined,
    countryCode: "",
    email: "",
    firstName: "",
    lastName: "",
    phoneNumber: "",
    join: false,
    zipCode: "",
    dateOfBirth: undefined,
    termsAccepted: false,
    membershipNo: "",
  }

  let inputUserData = {}
  if (sessionData) {
    inputUserData = JSON.parse(sessionData)
  }

  const validPaths = [StepEnum.selectBed]

  let initialData: EnterDetailsState["userData"] = defaultUserData

  const isValid = {
    [StepEnum.selectBed]: false,
    [StepEnum.breakfast]: false,
    [StepEnum.details]: false,
    [StepEnum.payment]: false,
  }

  const validatedBedType = bedTypeSchema.safeParse(inputUserData)
  if (validatedBedType.success) {
    validPaths.push(StepEnum.breakfast)
    initialData = { ...initialData, ...validatedBedType.data }
    isValid[StepEnum.selectBed] = true
  }
  const validatedBreakfast = breakfastStoreSchema.safeParse(inputUserData)
  if (validatedBreakfast.success) {
    validPaths.push(StepEnum.details)
    initialData = { ...initialData, ...validatedBreakfast.data }
    isValid[StepEnum.breakfast] = true
  }
  const detailsSchema = isMember ? signedInDetailsSchema : guestDetailsSchema
  const validatedDetails = detailsSchema.safeParse(inputUserData)
  if (validatedDetails.success) {
    validPaths.push(StepEnum.payment)
    initialData = { ...initialData, ...validatedDetails.data }
    isValid[StepEnum.details] = true
  }

  if (!validPaths.includes(currentStep)) {
    currentStep = validPaths.pop()! // We will always have at least one valid path
    if (isBrowser) {
      window.history.pushState(
        { step: currentStep },
        "",
        currentStep + window.location.search
      )
    }
  }

  return create<EnterDetailsState>()((set, get) => ({
    userData: initialData,
    roomData,
    selectRateUrl,
    steps: Object.values(StepEnum),
    totalPrice: {
      local: { price: 0, currency: "" },
      euro: { price: 0, currency: "" },
    },
    isSummaryOpen: false,
    isSubmittingDisabled: false,
    setCurrentStep: (step) => set({ currentStep: step }),
    navigate: (step, updatedData) =>
      set(
        produce((state) => {
          const sessionStorage = window.sessionStorage

          const previousDataString = sessionStorage.getItem(SESSION_STORAGE_KEY)

          const previousData = JSON.parse(previousDataString || "{}")

          sessionStorage.setItem(
            SESSION_STORAGE_KEY,
            JSON.stringify({ ...previousData, ...updatedData })
          )

          state.currentStep = step
          window.history.pushState({ step }, "", step + window.location.search)
        })
      ),
    currentStep,
    isValid,
    completeStep: (updatedData) =>
      set(
        produce((state: EnterDetailsState) => {
          state.isValid[state.currentStep] = true

          const nextStep =
            state.steps[state.steps.indexOf(state.currentStep) + 1]

          state.userData = {
            ...state.userData,
            ...updatedData,
          }
          state.currentStep = nextStep
          get().navigate(nextStep, updatedData)
        })
      ),
    toggleSummaryOpen: () => set({ isSummaryOpen: !get().isSummaryOpen }),
    setTotalPrice: (totalPrice) => set({ totalPrice: totalPrice }),
    setIsSubmittingDisabled: (isSubmittingDisabled) =>
      set({ isSubmittingDisabled }),
  }))
}

export type EnterDetailsStore = ReturnType<typeof initEditDetailsState>

export const EnterDetailsContext = createContext<EnterDetailsStore | null>(null)

export const useEnterDetailsStore = <T>(
  selector: (store: EnterDetailsState) => T
): T => {
  const enterDetailsContextStore = useContext(EnterDetailsContext)

  if (!enterDetailsContextStore) {
    throw new Error(
      `useEnterDetailsStore must be used within EnterDetailsContextProvider`
    )
  }

  return useStore(enterDetailsContextStore, selector)
}
