import * as api from "@/lib/api"
import { badRequestError, serverErrorByStatus } from "@/server/errors/trpc"
import { createCounter } from "@/server/telemetry"
import { toApiLang } from "@/server/utils"

import { bookingConfirmationSchema, createBookingSchema } from "./output"

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

export async function getBooking(
  confirmationNumber: string,
  lang: Lang,
  token: string
) {
  const getBookingCounter = createCounter("booking", "get")
  const metricsGetBooking = getBookingCounter.init({ confirmationNumber })

  metricsGetBooking.start()

  const apiResponse = await api.get(
    api.endpoints.v1.Booking.booking(confirmationNumber),
    {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    },
    { language: toApiLang(lang) }
  )

  if (!apiResponse.ok) {
    await metricsGetBooking.httpError(apiResponse)

    // If the booking is not found, return null.
    // This scenario is expected to happen when a logged in user trying to access a booking that doesn't belong to them.
    if (apiResponse.status === 400) {
      return null
    }

    throw serverErrorByStatus(apiResponse.status, apiResponse)
  }

  const apiJson = await apiResponse.json()
  const booking = bookingConfirmationSchema.safeParse(apiJson)
  if (!booking.success) {
    metricsGetBooking.validationError(booking.error)
    throw badRequestError()
  }

  metricsGetBooking.success()

  return booking.data
}

export async function findBooking(
  confirmationNumber: string,
  lang: Lang,
  token: string,
  lastName?: string,
  firstName?: string,
  email?: string
) {
  const findBookingCounter = createCounter("booking", "find")
  const metricsGetBooking = findBookingCounter.init({
    confirmationNumber,
    lastName,
    firstName,
    email,
  })

  metricsGetBooking.start()

  const apiResponse = await api.post(
    api.endpoints.v1.Booking.find(confirmationNumber),
    {
      headers: {
        Authorization: `Bearer ${token}`,
      },
      body: {
        lastName,
        firstName,
        email,
      },
    },
    { language: toApiLang(lang) }
  )

  if (!apiResponse.ok) {
    await metricsGetBooking.httpError(apiResponse)

    // If the booking is not found, return null.
    // This scenario is expected to happen when a logged in user trying to access a booking that doesn't belong to them.
    if (apiResponse.status === 400) {
      return null
    }

    throw serverErrorByStatus(apiResponse.status, apiResponse)
  }

  const apiJson = await apiResponse.json()
  const booking = bookingConfirmationSchema.safeParse(apiJson)
  if (!booking.success) {
    metricsGetBooking.validationError(booking.error)
    throw badRequestError()
  }

  metricsGetBooking.success()

  return booking.data
}

export async function cancelBooking(
  confirmationNumber: string,
  language: Lang,
  token: string
) {
  const cancelBookingCounter = createCounter("booking", "cancel")
  const metricsCancelBooking = cancelBookingCounter.init({
    confirmationNumber,
    language,
  })

  metricsCancelBooking.start()

  const headers = {
    Authorization: `Bearer ${token}`,
  }

  const booking = await getBooking(confirmationNumber, language, token)
  if (!booking) {
    metricsCancelBooking.noDataError({ confirmationNumber })
    return null
  }
  const { firstName, lastName, email } = booking.guest
  const apiResponse = await api.remove(
    api.endpoints.v1.Booking.cancel(confirmationNumber),
    {
      headers,
      body: { firstName, lastName, email },
    },
    { language: toApiLang(language) }
  )

  if (!apiResponse.ok) {
    await metricsCancelBooking.httpError(apiResponse)
    return null
  }

  const apiJson = await apiResponse.json()
  const verifiedData = createBookingSchema.safeParse(apiJson)
  if (!verifiedData.success) {
    metricsCancelBooking.validationError(verifiedData.error)
    return null
  }

  metricsCancelBooking.success()

  return verifiedData.data
}
