import { notFound } from "next/navigation"

import { PaymentCallbackStatusEnum } from "@scandic-hotels/common/constants/paymentCallbackStatusEnum"
import {
  bookingConfirmation,
  details,
} from "@scandic-hotels/common/constants/routes/hotelReservation"
import { logger } from "@scandic-hotels/common/logger"
import { getServiceToken } from "@scandic-hotels/common/tokenManager"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { getBooking } from "@scandic-hotels/trpc/routers/booking/utils"
import { encrypt } from "@scandic-hotels/trpc/utils/encryption"

import { HandleErrorCallback } from "../components/EnterDetails/Payment/PaymentCallback/HandleErrorCallback"
import { HandleSuccessCallback } from "../components/EnterDetails/Payment/PaymentCallback/HandleSuccessCallback"
import { serverClient } from "../trpc"

import type { Lang } from "@scandic-hotels/common/constants/language"

import type { NextSearchParams } from "../types"

type PaymentCallbackPageProps = {
  lang: Lang
  searchParams: NextSearchParams
  userAccessToken: string | null
  status: PaymentCallbackStatusEnum
}
export async function PaymentCallbackPage({
  lang,
  userAccessToken,
  searchParams,
  status,
}: PaymentCallbackPageProps) {
  const { confirmationNumber } = searchParams

  if (
    !status ||
    !confirmationNumber ||
    typeof confirmationNumber !== "string"
  ) {
    logger.error(
      `[payment-callback] missing status or confirmationNumber in search params`
    )
    notFound()
  }

  const returnUrl = details(lang)
  const searchObject = new URLSearchParams()
  let errorMessage = undefined

  if (status === PaymentCallbackStatusEnum.Cancel) {
    searchObject.set("errorCode", BookingErrorCodeEnum.TransactionCancelled)
    return (
      <HandleErrorCallback
        returnUrl={returnUrl.toString()}
        searchObject={searchObject}
        status={status}
        errorMessage={errorMessage}
      />
    )
  }

  let token = userAccessToken
  if (!token) {
    const serviceToken = await getServiceToken()
    if (serviceToken) {
      token = serviceToken.access_token
    }
  }

  if (!token) {
    logger.error(
      `[payment-callback] no token found for user, cannot fetch booking`
    )
    notFound()
  }

  const booking = await getBooking(confirmationNumber, lang, token)

  const refId = booking?.refId

  if (
    status === PaymentCallbackStatusEnum.Success &&
    confirmationNumber &&
    refId
  ) {
    const expire = Math.floor(Date.now() / 1000) + 60
    const sig = encrypt(expire.toString())
    const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(refId)}`
    logger.debug(
      `[payment-callback] rendering success callback with confirmation number: ${confirmationNumber}`
    )

    return (
      <HandleSuccessCallback
        refId={refId}
        sig={sig}
        successRedirectUrl={confirmationUrl}
        cardType={booking.guaranteeInfo?.cardType}
      />
    )
  }

  if (refId) {
    try {
      const caller = await serverClient()
      const bookingStatus = await caller.booking.status({
        refId,
      })

      const { booking } = bookingStatus

      // TODO: how to handle errors for multiple rooms?
      const error = booking.errors.find((e) => e.errorCode)

      errorMessage =
        error?.description ??
        `No error message found for booking ${confirmationNumber}, status: ${status}`

      searchObject.set(
        "errorCode",
        error
          ? error.errorCode.toString()
          : BookingErrorCodeEnum.TransactionFailed
      )
    } catch {
      logger.error(
        `[payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
      )
      searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
      errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
    }
  }

  if (status === PaymentCallbackStatusEnum.Error) {
    logger.error(
      `[payment-callback] error status received for ${confirmationNumber}, status: ${status}`
    )
    searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
    errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
  }

  return (
    <HandleErrorCallback
      returnUrl={returnUrl.toString()}
      searchObject={searchObject}
      status={status}
      errorMessage={errorMessage}
    />
  )
}
