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

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

import EnterDetailsProvider from "@/providers/EnterDetailsProvider"

import { detailsStorageName, useEnterDetailsStore } from "."

import { BreakfastPackageEnum } from "@/types/enums/breakfast"
import { CurrencyEnum } from "@/types/enums/currency"
import { PackageTypeEnum } from "@/types/enums/packages"
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),
}))

const booking = {
  hotelId: "123",
  fromDate: "2100-01-01",
  toDate: "2100-01-02",
  rooms: [
    {
      adults: 1,
      roomTypeCode: "SKS",
      rateCode: "SAVEEU",
      counterRateCode: "PLSA2BEU",
    },
  ],
}

const bedTypes = [
  {
    type: BedTypeEnum.King,
    description: "King-size bed",
    value: "SKS",
    size: {
      min: 180,
      max: 200,
    },
    roomTypeCode: "SKS",
    extraBed: undefined,
  },
  {
    type: BedTypeEnum.Queen,
    description: "Queen-size bed",
    value: "QZ",
    size: {
      min: 160,
      max: 200,
    },
    roomTypeCode: "QZ",
    extraBed: undefined,
  },
]

const guest = {
  countryCode: "SE",
  dateOfBirth: "",
  email: "test@test.com",
  firstName: "Tester",
  lastName: "Testersson",
  join: false,
  membershipNo: "12345678901234",
  phoneNumber: "+46700000000",
  zipCode: "",
}

const breakfastPackages = [
  {
    code: BreakfastPackageEnum.REGULAR_BREAKFAST,
    description: "Breakfast with reservation",
    localPrice: {
      currency: CurrencyEnum.SEK,
      price: "99",
      totalPrice: "99",
    },
    requestedPrice: {
      currency: CurrencyEnum.EUR,
      price: "9",
      totalPrice: "9",
    },
    packageType: PackageTypeEnum.BreakfastAdult as const,
  },
]

function Wrapper({ children }: PropsWithChildren) {
  return (
    <EnterDetailsProvider
      bedTypes={bedTypes}
      booking={booking}
      showBreakfastStep={true}
      packages={null}
      roomRate={{
        memberRate: {
          rateCode: "PLSA2BEU",
          localPrice: {
            currency: CurrencyEnum.EUR,
            pricePerNight: 100,
            pricePerStay: 200,
          },
        },
        publicRate: {
          rateCode: "SAVEEU",
          localPrice: {
            currency: CurrencyEnum.EUR,
            pricePerNight: 100,
            pricePerStay: 200,
          },
        },
      }}
      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: bedTypes[1],
      breakfast: breakfastPackages[0],
      booking,
      guest,
    }

    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(bedTypes[0])
    })

    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(breakfastPackages[0])
    })

    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(guest)
    })

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