import { describe, expect, test } from "@jest/globals"
import { act, renderHook } from "@testing-library/react"
import { type PropsWithChildren } from "react"

import { Lang } from "@/constants/languages"

import {
  bedType,
  booking,
  breakfastPackage,
  guestDetailsNonMember,
  roomRate,
} from "@/__mocks__/hotelReservation"
import EnterDetailsProvider from "@/providers/EnterDetailsProvider"

import { detailsStorageName, useEnterDetailsStore } from "."

import { StepEnum } from "@/types/enums/step"
import type { PersistedState } from "@/types/stores/enter-details"

jest.mock("react", () => ({
  ...jest.requireActual("react"),
  cache: jest.fn(),
}))

jest.mock("@/server/utils", () => ({
  toLang: () => Lang.en,
}))

jest.mock("@/lib/api", () => ({
  fetchRetry: jest.fn((fn) => fn),
}))

function Wrapper({ children }: PropsWithChildren) {
  return (
    <EnterDetailsProvider
      bedTypes={[bedType.king, bedType.queen]}
      booking={booking}
      showBreakfastStep={true}
      packages={null}
      roomRate={roomRate}
      searchParamsStr=""
      step={StepEnum.selectBed}
      user={null}
      vat={0}
    >
      {children}
    </EnterDetailsProvider>
  )
}

describe("Enter Details Store", () => {
  beforeEach(() => {
    window.sessionStorage.clear()
  })

  test("initialize with correct default values", () => {
    const { result } = renderHook(
      () => useEnterDetailsStore((state) => state),
      {
        wrapper: Wrapper,
      }
    )
    const state = result.current

    expect(state.currentStep).toBe(StepEnum.selectBed)
    expect(state.booking).toEqual(booking)
    expect(state.bedType).toEqual(undefined)
    expect(state.breakfast).toEqual(undefined)
    expect(Object.values(state.guest).every((value) => value === ""))
  })

  test("initialize with correct values from sessionStorage", async () => {
    const storage: PersistedState = {
      bedType: {
        roomTypeCode: bedType.queen.value,
        description: bedType.queen.description,
      },
      breakfast: breakfastPackage,
      booking,
      guest: guestDetailsNonMember,
    }

    window.sessionStorage.setItem(detailsStorageName, JSON.stringify(storage))

    const { result } = renderHook(
      () => useEnterDetailsStore((state) => state),
      {
        wrapper: Wrapper,
      }
    )
    const state = result.current

    expect(state.bedType).toEqual(storage.bedType)
    expect(state.guest).toEqual(storage.guest)
    expect(state.booking).toEqual(storage.booking)
    expect(state.breakfast).toEqual(storage.breakfast)
  })

  test("complete step and navigate to next step", async () => {
    const { result } = renderHook(
      () => useEnterDetailsStore((state) => state),
      {
        wrapper: Wrapper,
      }
    )

    expect(result.current.currentStep).toEqual(StepEnum.selectBed)

    await act(async () => {
      result.current.actions.updateBedType({
        roomTypeCode: bedType.king.value,
        description: bedType.king.description,
      })
    })

    expect(result.current.isValid[StepEnum.selectBed]).toEqual(true)
    expect(result.current.currentStep).toEqual(StepEnum.breakfast)
    expect(window.location.pathname.slice(1)).toBe(StepEnum.breakfast)

    await act(async () => {
      result.current.actions.updateBreakfast(breakfastPackage)
    })

    expect(result.current.isValid[StepEnum.breakfast]).toEqual(true)
    expect(result.current.currentStep).toEqual(StepEnum.details)
    expect(window.location.pathname.slice(1)).toBe(StepEnum.details)

    await act(async () => {
      result.current.actions.updateDetails(guestDetailsNonMember)
    })

    expect(result.current.isValid[StepEnum.details]).toEqual(true)
    expect(result.current.currentStep).toEqual(StepEnum.payment)
    expect(window.location.pathname.slice(1)).toBe(StepEnum.payment)
  })
})
