import { notFound } from "next/navigation"

import { PaymentCallbackStatusEnum } from "@scandic-hotels/common/constants/paymentCallbackStatusEnum"
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
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/services/booking/getBooking"
import { encrypt } from "@scandic-hotels/trpc/utils/encryption"

import { BookingFlowConfig } from "../bookingFlowConfig/bookingFlowConfig"
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 { BookingStatus } from "@scandic-hotels/trpc/services/booking/getBookingStatus"

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

type PaymentCallbackPageProps = {
  lang: Lang
  searchParams: NextSearchParams
  userAccessToken: string | null
  status: PaymentCallbackStatusEnum
  config: BookingFlowConfig
}
export async function PaymentCallbackPage({
  lang,
  userAccessToken,
  searchParams,
  status,
  config,
}: 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)

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

  if (status === PaymentCallbackStatusEnum.Error) {
    logger.error(
      `[payment-callback] error status received for ${confirmationNumber}, status: ${status}`
    )
    const searchObject = new URLSearchParams()
    searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
    const errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
    return (
      <BookingFlowConfig config={config}>
        <HandleErrorCallback
          returnUrl={returnUrl.toString()}
          searchObject={searchObject}
          status={status}
          errorMessage={errorMessage}
        />
      </BookingFlowConfig>
    )
  }

  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

  const caller = await serverClient()
  const bookingStatus = refId
    ? await caller.booking.status({
        refId,
      })
    : null

  if (status === PaymentCallbackStatusEnum.Success && refId) {
    const shouldValidatePayment =
      bookingStatus?.booking.paymentMethod === PaymentMethodEnum.PartnerPoints
    if (shouldValidatePayment) {
      // TODO We probably need better error handling for this mutation,
      // but for now we've just implemented the happy path
      await caller.booking.validatePartnerPayment({ confirmationNumber })
    }

    // eslint-disable-next-line react-hooks/purity
    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 (
      <BookingFlowConfig config={config}>
        <HandleSuccessCallback
          refId={refId}
          sig={sig}
          successRedirectUrl={confirmationUrl}
          cardType={booking.guaranteeInfo?.cardType}
        />
      </BookingFlowConfig>
    )
  }

  return (
    <HandleBookingStatusError
      status={status}
      booking={bookingStatus?.booking ?? null}
      returnUrl={returnUrl.toString()}
      confirmationNumber={confirmationNumber}
      config={config}
    />
  )
}

function HandleBookingStatusError({
  booking,
  confirmationNumber,
  returnUrl,
  config,
  status,
}: {
  booking: BookingStatus | null
  confirmationNumber?: string
  returnUrl: string
  config: BookingFlowConfig
  status: PaymentCallbackStatusEnum
}) {
  if (!booking) {
    logger.error(
      `[payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
    )
    const searchObject = new URLSearchParams()
    searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
    const errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`

    return (
      <BookingFlowConfig config={config}>
        <HandleErrorCallback
          returnUrl={returnUrl}
          searchObject={searchObject}
          status={status}
          errorMessage={errorMessage}
        />
      </BookingFlowConfig>
    )
  }

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

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

  const searchObject = new URLSearchParams()
  searchObject.set(
    "errorCode",
    error ? error.errorCode.toString() : BookingErrorCodeEnum.TransactionFailed
  )

  return (
    <BookingFlowConfig config={config}>
      <HandleErrorCallback
        returnUrl={returnUrl}
        searchObject={searchObject}
        status={status}
        errorMessage={errorMessage}
      />
    </BookingFlowConfig>
  )
}
