"use client"

import { zodResolver } from "@hookform/resolvers/zod"
import { useRouter, useSearchParams } from "next/navigation"
import { useCallback, useEffect, useState } from "react"
import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl"

import {
  BOOKING_CONFIRMATION_NUMBER,
  BookingErrorCodeEnum,
  BookingStatusEnum,
  PAYMENT_METHOD_TITLES,
  PaymentMethodEnum,
} from "@/constants/booking"
import {
  bookingConfirmation,
  selectRate,
} from "@/constants/routes/hotelReservation"
import { env } from "@/env/client"
import { trpc } from "@/lib/trpc/client"
import { useEnterDetailsStore } from "@/stores/enter-details"

import LoadingSpinner from "@/components/LoadingSpinner"
import Button from "@/components/TempDesignSystem/Button"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import { useAvailablePaymentOptions } from "@/hooks/booking/useAvailablePaymentOptions"
import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus"
import useLang from "@/hooks/useLang"
import { trackPaymentEvent } from "@/utils/tracking"

import { bedTypeMap } from "../../utils"
import ConfirmBooking from "../Confirm"
import PriceChangeDialog from "../PriceChangeDialog"
import GuaranteeDetails from "./GuaranteeDetails"
import { hasFlexibleRate, hasPrepaidRate, isPaymentMethodEnum } from "./helpers"
import MixedRatePaymentBreakdown from "./MixedRatePaymentBreakdown"
import MySavedCards from "./MySavedCards"
import PaymentAlert from "./PaymentAlert"
import PaymentOption from "./PaymentOption"
import { type PaymentFormData, paymentSchema } from "./schema"
import TermsAndConditions from "./TermsAndConditions"

import styles from "./payment.module.css"

import type {
  PaymentClientProps,
  PriceChangeData,
} from "@/types/components/hotelReservation/enterDetails/payment"
import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"

const maxRetries = 15
const retryInterval = 2000

export const formId = "submit-booking"

export default function PaymentClient({
  otherPaymentOptions,
  savedCreditCards,
  mustBeGuaranteed,
  memberMustBeGuaranteed,
  isFlexRate,
}: PaymentClientProps) {
  const router = useRouter()
  const lang = useLang()
  const intl = useIntl()
  const searchParams = useSearchParams()

  const [showPaymentAlert, setShowPaymentAlert] = useState(false)

  const { booking, canProceedToPayment, rooms, totalPrice } =
    useEnterDetailsStore((state) => ({
      booking: state.booking,
      canProceedToPayment: state.canProceedToPayment,
      rooms: state.rooms,
      totalPrice: state.totalPrice,
    }))

  const bookingMustBeGuaranteed = rooms.some(
    ({ room }, idx) =>
      (room.guest.join || room.guest.membershipNo) &&
      booking.rooms[idx].counterRateCode
  )
    ? memberMustBeGuaranteed
    : mustBeGuaranteed

  const setIsSubmittingDisabled = useEnterDetailsStore(
    (state) => state.actions.setIsSubmittingDisabled
  )

  const [bookingNumber, setBookingNumber] = useState<string>("")
  const [isPollingForBookingStatus, setIsPollingForBookingStatus] =
    useState(false)

  const availablePaymentOptions =
    useAvailablePaymentOptions(otherPaymentOptions)
  const [priceChangeData, setPriceChangeData] =
    useState<PriceChangeData | null>(null)

  const { toDate, fromDate, hotelId } = booking

  const hasPrepaidRates = rooms.some(hasPrepaidRate)
  const hasFlexRates = rooms.some(hasFlexibleRate)
  const hasMixedRates = hasPrepaidRates && hasFlexRates

  const methods = useForm<PaymentFormData>({
    defaultValues: {
      paymentMethod: savedCreditCards?.length
        ? savedCreditCards[0].id
        : PaymentMethodEnum.card,
      smsConfirmation: false,
      termsAndConditions: false,
      guarantee: false,
    },
    mode: "all",
    reValidateMode: "onChange",
    resolver: zodResolver(paymentSchema),
  })

  const initiateBooking = trpc.booking.create.useMutation({
    onSuccess: (result) => {
      if (result) {
        if ("error" in result) {
          if (result.cause === BookingErrorCodeEnum.AvailabilityError) {
            window.location.reload() // reload to refetch room data because we dont know which room is unavailable
          } else {
            handlePaymentError(result.cause)
          }
          return
        }

        if (result.reservationStatus == BookingStatusEnum.BookingCompleted) {
          const confirmationUrl = `${bookingConfirmation(lang)}?${BOOKING_CONFIRMATION_NUMBER}=${result.id}`
          router.push(confirmationUrl)
        }

        setBookingNumber(result.id)

        const hasPriceChange = result.rooms.some((r) => r.priceChangedMetadata)
        if (hasPriceChange) {
          const priceChangeData = result.rooms.map(
            (room) => room.priceChangedMetadata || null
          )
          setPriceChangeData(priceChangeData)
        } else {
          setIsPollingForBookingStatus(true)
        }
      } else {
        handlePaymentError("No confirmation number")
      }
    },
    onError: (error) => {
      console.error("Error", error)
      handlePaymentError(error.message)
    },
  })

  const priceChange = trpc.booking.priceChange.useMutation({
    onSuccess: (result) => {
      if (result?.id) {
        setIsPollingForBookingStatus(true)
      } else {
        handlePaymentError("No confirmation number")
      }
      setPriceChangeData(null)
    },
    onError: (error) => {
      console.error("Error", error)
      setPriceChangeData(null)
      handlePaymentError(error.message)
    },
  })

  const bookingStatus = useHandleBookingStatus({
    confirmationNumber: bookingNumber,
    expectedStatuses: [BookingStatusEnum.BookingCompleted],
    maxRetries,
    retryInterval,
    enabled: isPollingForBookingStatus,
  })

  const handlePaymentError = useCallback(
    (errorMessage: string) => {
      setShowPaymentAlert(true)

      const currentPaymentMethod = methods.getValues("paymentMethod")
      const smsEnable = methods.getValues("smsConfirmation")
      const isSavedCreditCard = savedCreditCards?.some(
        (card) => card.id === currentPaymentMethod
      )

      trackPaymentEvent({
        event: "paymentFail",
        hotelId,
        method: currentPaymentMethod,
        isSavedCreditCard,
        smsEnable,
        errorMessage,
        status: "failed",
      })
    },
    [methods, savedCreditCards, hotelId]
  )

  useEffect(() => {
    if (bookingStatus?.data?.paymentUrl) {
      router.push(bookingStatus.data.paymentUrl)
    } else if (bookingStatus.isTimeout) {
      handlePaymentError("Timeout")
    }
  }, [bookingStatus, router, intl, handlePaymentError])

  useEffect(() => {
    setIsSubmittingDisabled(
      !methods.formState.isValid || methods.formState.isSubmitting
    )
  }, [
    methods.formState.isValid,
    methods.formState.isSubmitting,
    setIsSubmittingDisabled,
  ])

  const getPaymentMethod = (
    isFlexRate: boolean,
    paymentMethod: string | null | undefined
  ): PaymentMethodEnum => {
    if (isFlexRate) {
      return PaymentMethodEnum.card
    }
    return paymentMethod && isPaymentMethodEnum(paymentMethod)
      ? paymentMethod
      : PaymentMethodEnum.card
  }

  const handleSubmit = useCallback(
    (data: PaymentFormData) => {
      const paymentMethod = getPaymentMethod(isFlexRate, data.paymentMethod)

      const savedCreditCard = savedCreditCards?.find(
        (card) => card.id === data.paymentMethod
      )

      const paymentRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}/${lang}/hotelreservation/payment-callback`
      const guarantee = data.guarantee
      const useSavedCard = savedCreditCard
        ? {
            card: {
              alias: savedCreditCard.alias,
              expiryDate: savedCreditCard.expirationDate,
              cardType: savedCreditCard.cardType,
            },
          }
        : {}

      const shouldUsePayment = !isFlexRate || guarantee

      const payment = shouldUsePayment
        ? {
            paymentMethod: paymentMethod,
            ...useSavedCard,
            success: `${paymentRedirectUrl}/success`,
            error: `${paymentRedirectUrl}/error`,
            cancel: `${paymentRedirectUrl}/cancel`,
          }
        : undefined

      trackPaymentEvent({
        event: "paymentAttemptStart",
        hotelId,
        method: paymentMethod,
        isSavedCreditCard: !!savedCreditCard,
        smsEnable: data.smsConfirmation,
        status: "attempt",
      })

      initiateBooking.mutate({
        checkInDate: fromDate,
        checkOutDate: toDate,
        hotelId,
        language: lang,
        payment,
        rooms: rooms.map(({ room }, idx) => ({
          adults: room.adults,
          childrenAges: room.childrenInRoom?.map((child) => ({
            age: child.age,
            bedType: bedTypeMap[parseInt(child.bed.toString())],
          })),
          guest: {
            becomeMember: room.guest.join,
            countryCode: room.guest.countryCode,
            email: room.guest.email,
            firstName: room.guest.firstName,
            lastName: room.guest.lastName,
            membershipNumber: room.guest.membershipNo,
            phoneNumber: room.guest.phoneNumber,
            // Only allowed for room one
            ...(idx === 0 && {
              dateOfBirth:
                "dateOfBirth" in room.guest && room.guest.dateOfBirth
                  ? room.guest.dateOfBirth
                  : undefined,
              postalCode:
                "zipCode" in room.guest && room.guest.zipCode
                  ? room.guest.zipCode
                  : undefined,
            }),
          },
          packages: {
            accessibility:
              room.roomFeatures?.some(
                (feature) =>
                  feature.code === RoomPackageCodeEnum.ACCESSIBILITY_ROOM
              ) ?? false,
            allergyFriendly:
              room.roomFeatures?.some(
                (feature) => feature.code === RoomPackageCodeEnum.ALLERGY_ROOM
              ) ?? false,
            breakfast: !!(room.breakfast && room.breakfast.code),
            petFriendly:
              room.roomFeatures?.some(
                (feature) => feature.code === RoomPackageCodeEnum.PET_ROOM
              ) ?? false,
          },
          rateCode:
            (room.guest.join || room.guest.membershipNo) &&
            booking.rooms[idx].counterRateCode
              ? booking.rooms[idx].counterRateCode
              : booking.rooms[idx].rateCode,
          roomPrice: {
            memberPrice: room.roomRate.memberRate?.localPrice.pricePerStay,
            publicPrice: room.roomRate.publicRate?.localPrice.pricePerStay,
          },
          bookingCode: booking.bookingCode,
          roomTypeCode: room.bedType!.roomTypeCode, // A selection has been made in order to get to this step.
          smsConfirmationRequested: data.smsConfirmation,
          specialRequest: {
            comment: room.specialRequest.comment
              ? room.specialRequest.comment
              : undefined,
          },
        })),
      })
    },
    [
      savedCreditCards,
      lang,
      initiateBooking,
      hotelId,
      fromDate,
      toDate,
      rooms,
      booking,
      isFlexRate,
    ]
  )

  if (
    initiateBooking.isPending ||
    (isPollingForBookingStatus &&
      !bookingStatus.data?.paymentUrl &&
      !bookingStatus.isTimeout)
  ) {
    return <LoadingSpinner />
  }

  const paymentGuarantee = intl.formatMessage({
    id: "Payment Guarantee",
  })
  const payment = intl.formatMessage({
    id: "Payment",
  })
  const confirm = intl.formatMessage({
    id: "Confirm booking",
  })

  return (
    <section
      className={`${styles.paymentSection} ${canProceedToPayment ? "" : styles.disabled}`}
    >
      <header>
        <Title level="h2" as="h4">
          {bookingMustBeGuaranteed
            ? paymentGuarantee
            : isFlexRate
              ? confirm
              : payment}
        </Title>
        <PaymentAlert isVisible={showPaymentAlert} />
      </header>
      <FormProvider {...methods}>
        <form
          className={styles.paymentContainer}
          onSubmit={methods.handleSubmit(handleSubmit)}
          id={formId}
        >
          {isFlexRate && !bookingMustBeGuaranteed ? (
            <ConfirmBooking savedCreditCards={savedCreditCards} />
          ) : (
            <>
              {bookingMustBeGuaranteed ? (
                <section className={styles.section}>
                  <Body>
                    {intl.formatMessage({
                      id: "To secure your reservation, we kindly ask you to provide your payment card details. Rest assured, no charges will be made at this time.",
                    })}
                  </Body>
                  <GuaranteeDetails />
                </section>
              ) : null}

              {hasMixedRates ? (
                <Body>
                  {intl.formatMessage({
                    id: "As your booking includes rooms with different terms, we will be charging part of the booking now and the remainder will be collected by the reception at check-in.",
                  })}
                </Body>
              ) : null}

              {savedCreditCards?.length ? (
                <section className={styles.section}>
                  <MySavedCards savedCreditCards={savedCreditCards} />
                </section>
              ) : null}

              <section className={styles.section}>
                {savedCreditCards?.length ? (
                  <Body color="uiTextHighContrast" textTransform="bold">
                    {intl.formatMessage({ id: "OTHER PAYMENT METHODS" })}
                  </Body>
                ) : null}
                <div className={styles.paymentOptionContainer}>
                  <PaymentOption
                    name="paymentMethod"
                    value={PaymentMethodEnum.card}
                    label={intl.formatMessage({ id: "Credit card" })}
                  />
                  {availablePaymentOptions.map((paymentMethod) => (
                    <PaymentOption
                      key={paymentMethod}
                      name="paymentMethod"
                      value={paymentMethod}
                      label={
                        PAYMENT_METHOD_TITLES[
                          paymentMethod as PaymentMethodEnum
                        ]
                      }
                    />
                  ))}
                </div>
                {hasMixedRates ? (
                  <MixedRatePaymentBreakdown
                    rooms={rooms}
                    currency={totalPrice.local.currency}
                  />
                ) : null}
              </section>

              <section className={styles.section}>
                <TermsAndConditions />
              </section>
            </>
          )}
          <div className={styles.submitButton}>
            <Button
              intent="primary"
              theme="base"
              size="small"
              type="submit"
              disabled={
                !methods.formState.isValid || methods.formState.isSubmitting
              }
            >
              {intl.formatMessage({ id: "Complete booking" })}
            </Button>
          </div>
        </form>
      </FormProvider>
      {priceChangeData ? (
        <PriceChangeDialog
          isOpen={!!priceChangeData}
          priceChangeData={priceChangeData}
          prevTotalPrice={totalPrice.local.price}
          currency={totalPrice.local.currency}
          onCancel={() => {
            const allSearchParams = searchParams.size
              ? `?${searchParams.toString()}`
              : ""
            router.push(`${selectRate(lang)}${allSearchParams}`)
          }}
          onAccept={() =>
            priceChange.mutate({ confirmationNumber: bookingNumber })
          }
        />
      ) : null}
    </section>
  )
}
