import { describe, expect, it } from "@jest/globals"

import accessBooking, {
  ACCESS_GRANTED,
  ERROR_BAD_REQUEST,
  ERROR_FORBIDDEN,
  ERROR_NOT_FOUND,
  ERROR_UNAUTHORIZED,
} from "./accessBooking"

import type { SafeUser } from "@/types/user"
import type { Guest } from "@/server/routers/booking/output"

describe("Access booking", () => {
  describe("for logged in booking", () => {
    it("should enable access if all is provided", () => {
      expect(accessBooking(loggedInGuest, "Booking", authenticatedUser)).toBe(
        ACCESS_GRANTED
      )
    })
    it("should enable access if all is provided and be case-insensitive", () => {
      expect(accessBooking(loggedInGuest, "BoOkInG", authenticatedUser)).toBe(
        ACCESS_GRANTED
      )
    })
    it("should prompt to login without user", () => {
      expect(accessBooking(loggedInGuest, "Booking", null)).toBe(
        ERROR_UNAUTHORIZED
      )
    })
    it("should prompt to login if user mismatch", () => {
      expect(
        accessBooking(loggedInGuest, "Booking", badAuthenticatedUser)
      ).toBe(ERROR_UNAUTHORIZED)
    })
    it("should deny access if refId mismatch", () => {
      expect(
        accessBooking(loggedInGuest, "NotBooking", authenticatedUser)
      ).toBe(ERROR_UNAUTHORIZED)
    })
  })

  describe("for anonymous booking", () => {
    it("should enable access if all is provided", () => {
      const cookieString = new URLSearchParams({
        confirmationNumber: "123456789",
        firstName: "Anonymous",
        lastName: "Booking",
        email: "logged+out@scandichotels.com",
      }).toString()
      expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe(
        ACCESS_GRANTED
      )
    })
    it("should enable access if all is provided and be case-insensitive for first name", () => {
      const cookieString = new URLSearchParams({
        confirmationNumber: "123456789",
        firstName: "AnOnYmOuS",
        lastName: "Booking",
        email: "logged+out@scandichotels.com",
      }).toString()
      expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe(
        ACCESS_GRANTED
      )
    })
    it("should enable access if all is provided and be case-insensitive for last name", () => {
      const cookieString = new URLSearchParams({
        confirmationNumber: "123456789",
        firstName: "Anonymous",
        lastName: "Booking",
        email: "logged+out@scandichotels.com",
      }).toString()
      expect(accessBooking(loggedOutGuest, "BoOkInG", null, cookieString)).toBe(
        ACCESS_GRANTED
      )
    })
    it("should enable access if all is provided and be case-insensitive for email", () => {
      const cookieString = new URLSearchParams({
        confirmationNumber: "123456789",
        firstName: "Anonymous",
        lastName: "Booking",
        email: "LOGGED+out@scandichotels.com",
      }).toString()
      expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe(
        ACCESS_GRANTED
      )
    })
    it("should prompt logout if user is logged in", () => {
      const cookieString = new URLSearchParams({
        confirmationNumber: "123456789",
        firstName: "Anonymous",
        lastName: "Booking",
        email: "logged+out@scandichotels.com",
      }).toString()
      expect(
        accessBooking(
          loggedOutGuest,
          "Booking",
          authenticatedUser,
          cookieString
        )
      ).toBe(ERROR_FORBIDDEN)
    })
    it("should prompt for more if first name is missing", () => {
      const cookieString = new URLSearchParams({
        confirmationNumber: "123456789",
        lastName: "Booking",
        email: "logged+out@scandichotels.com",
      }).toString()
      expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe(
        ERROR_BAD_REQUEST
      )
    })
    it("should prompt for more if email is missing", () => {
      const cookieString = new URLSearchParams({
        confirmationNumber: "123456789",
        firstName: "Anonymous",
        lastName: "Booking",
      }).toString()
      expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe(
        ERROR_BAD_REQUEST
      )
    })
    it("should prompt for more if cookie is invalid", () => {
      const cookieString = new URLSearchParams({}).toString()
      expect(accessBooking(loggedOutGuest, "Booking", null, cookieString)).toBe(
        ERROR_BAD_REQUEST
      )
    })
    it("should deny access if refId mismatch", () => {
      expect(accessBooking(loggedOutGuest, "NotBooking", null)).toBe(
        ERROR_NOT_FOUND
      )
    })
  })
})

const authenticatedUser: SafeUser = {
  address: {
    city: undefined,
    country: "Sweden",
    countryCode: "SE",
    streetAddress: undefined,
    zipCode: undefined,
  },
  dateOfBirth: "",
  email: "",
  firstName: "Authenticated",
  language: undefined,
  lastName: "Booking",
  membershipNumber: "01234567890123",
  membership: null,
  loyalty: {
    memberships: [],
    pointExpirations: [],
    points: {
      earned: 0,
      spent: 0,
      spendable: 0,
    },
    tier: "L1",
    tierExpires: "",
  },
  name: "",
  phoneNumber: undefined,
  profileId: "",
}

const badAuthenticatedUser: SafeUser = {
  address: {
    city: undefined,
    country: "Sweden",
    countryCode: "SE",
    streetAddress: undefined,
    zipCode: undefined,
  },
  dateOfBirth: "",
  email: "",
  firstName: "Authenticated",
  language: undefined,
  lastName: `Bad name ${Math.random()}`,
  membershipNumber: "0987654321",
  membership: null,
  loyalty: {
    memberships: [],
    pointExpirations: [],
    points: {
      earned: 0,
      spent: 0,
      spendable: 0,
    },
    tier: "L1",
    tierExpires: "",
  },
  name: "",
  phoneNumber: undefined,
  profileId: "",
}

const loggedOutGuest: Guest = {
  email: "logged+out@scandichotels.com",
  firstName: "Anonymous",
  lastName: "Booking",
  membershipNumber: "",
  phoneNumber: "+46701234567",
  countryCode: "SE",
}

const loggedInGuest: Guest = {
  email: "logged+in@scandichotels.com",
  firstName: "Authenticated",
  lastName: "Booking",
  membershipNumber: "01234567890123",
  phoneNumber: "+46701234567",
  countryCode: "SE",
}
