"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 {
  BookingStatusEnum,
  PAYMENT_METHOD_TITLES,
  PaymentMethodEnum,
} from "@/constants/booking"
import {
  bookingTermsAndConditions,
  privacyPolicy,
} from "@/constants/currentWebHrefs"
import { 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 Checkbox from "@/components/TempDesignSystem/Form/Checkbox"
import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import { toast } from "@/components/TempDesignSystem/Toasts"
import { useAvailablePaymentOptions } from "@/hooks/booking/useAvailablePaymentOptions"
import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus"
import { usePaymentFailedToast } from "@/hooks/booking/usePaymentFailedToast"
import useLang from "@/hooks/useLang"
import { trackPaymentEvent } from "@/utils/tracking"

import { bedTypeMap } from "../../SelectRate/RoomSelection/utils"
import PriceChangeDialog from "../PriceChangeDialog"
import GuaranteeDetails from "./GuaranteeDetails"
import PaymentOption from "./PaymentOption"
import { type PaymentFormData, paymentSchema } from "./schema"

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

import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import type { PaymentClientProps } from "@/types/components/hotelReservation/selectRate/section"

const maxRetries = 4
const retryInterval = 2000

export const formId = "submit-booking"

function isPaymentMethodEnum(value: string): value is PaymentMethodEnum {
  return Object.values(PaymentMethodEnum).includes(value as PaymentMethodEnum)
}

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

  const totalPrice = useEnterDetailsStore((state) => state.totalPrice)
  const { bedType, booking, breakfast } = useEnterDetailsStore((state) => ({
    bedType: state.bedType,
    booking: state.booking,
    breakfast: state.breakfast,
  }))
  const userData = useEnterDetailsStore((state) => state.guest)
  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<{
    oldPrice: number
    newPrice: number
  } | null>()

  const { toDate, fromDate, rooms, hotel } = booking

  usePaymentFailedToast()

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

  const initiateBooking = trpc.booking.create.useMutation({
    onSuccess: (result) => {
      if (result) {
        setBookingNumber(result.id)

        const priceChange = result.rooms.find(
          (r) => r.priceChangedMetadata
        )?.priceChangedMetadata

        if (priceChange) {
          setPriceChangeData({
            oldPrice: roomPrice.publicPrice,
            newPrice: priceChange.totalPrice,
          })
        } 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,
    expectedStatus: BookingStatusEnum.BookingCompleted,
    maxRetries,
    retryInterval,
    enabled: isPollingForBookingStatus,
  })

  const handlePaymentError = useCallback(
    (errorMessage: string) => {
      toast.error(
        intl.formatMessage({
          id: "We had an issue processing your booking. Please try again. No charges have been made.",
        })
      )
      const currentPaymentMethod = methods.getValues("paymentMethod")
      const smsEnable = methods.getValues("smsConfirmation")
      const isSavedCreditCard = savedCreditCards?.some(
        (card) => card.id === currentPaymentMethod
      )

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

  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 handleSubmit = useCallback(
    (data: PaymentFormData) => {
      const {
        firstName,
        lastName,
        email,
        phoneNumber,
        countryCode,
        membershipNo,
        join,
        dateOfBirth,
        zipCode,
      } = userData

      // set payment method to card if saved card is submitted
      const paymentMethod = isPaymentMethodEnum(data.paymentMethod)
        ? data.paymentMethod
        : PaymentMethodEnum.card

      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`

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

      initiateBooking.mutate({
        language: lang,
        hotelId: hotel,
        checkInDate: fromDate,
        checkOutDate: toDate,
        rooms: rooms.map((room) => ({
          adults: room.adults,
          childrenAges: room.children?.map((child) => ({
            age: child.age,
            bedType: bedTypeMap[parseInt(child.bed.toString())],
          })),
          rateCode:
            (user || join || membershipNo) && room.counterRateCode
              ? room.counterRateCode
              : room.rateCode,
          roomTypeCode: bedType!.roomTypeCode, // A selection has been made in order to get to this step.
          guest: {
            firstName,
            lastName,
            email,
            phoneNumber,
            countryCode,
            membershipNumber: membershipNo,
            becomeMember: join,
            dateOfBirth,
            postalCode: zipCode,
          },
          packages: {
            breakfast: !!(breakfast && breakfast.code),
            allergyFriendly:
              room.packages?.includes(RoomPackageCodeEnum.ALLERGY_ROOM) ??
              false,
            petFriendly:
              room.packages?.includes(RoomPackageCodeEnum.PET_ROOM) ?? false,
            accessibility:
              room.packages?.includes(RoomPackageCodeEnum.ACCESSIBILITY_ROOM) ??
              false,
          },
          smsConfirmationRequested: data.smsConfirmation,
          roomPrice,
        })),
        payment: {
          paymentMethod,
          card: savedCreditCard
            ? {
                alias: savedCreditCard.alias,
                expiryDate: savedCreditCard.expirationDate,
                cardType: savedCreditCard.cardType,
              }
            : undefined,

          success: `${paymentRedirectUrl}/success`,
          error: `${paymentRedirectUrl}/error`,
          cancel: `${paymentRedirectUrl}/cancel`,
        },
      })
    },
    [
      userData,
      savedCreditCards,
      lang,
      initiateBooking,
      hotel,
      fromDate,
      toDate,
      rooms,
      user,
      bedType,
      breakfast,
      roomPrice,
    ]
  )

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

  return (
    <>
      <FormProvider {...methods}>
        <form
          className={styles.paymentContainer}
          onSubmit={methods.handleSubmit(handleSubmit)}
          id={formId}
        >
          {mustBeGuaranteed ? (
            <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}
          {savedCreditCards?.length ? (
            <section className={styles.section}>
              <Body color="uiTextHighContrast" textTransform="bold">
                {intl.formatMessage({ id: "MY SAVED CARDS" })}
              </Body>
              <div className={styles.paymentOptionContainer}>
                {savedCreditCards?.map((savedCreditCard) => (
                  <PaymentOption
                    key={savedCreditCard.id}
                    name="paymentMethod"
                    value={savedCreditCard.id}
                    label={
                      PAYMENT_METHOD_TITLES[
                        savedCreditCard.cardType as PaymentMethodEnum
                      ]
                    }
                    cardNumber={savedCreditCard.truncatedNumber}
                    hotelId={hotel}
                  />
                ))}
              </div>
            </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" })}
                hotelId={hotel}
              />
              {availablePaymentOptions.map((paymentMethod) => (
                <PaymentOption
                  key={paymentMethod}
                  name="paymentMethod"
                  value={paymentMethod}
                  label={
                    PAYMENT_METHOD_TITLES[paymentMethod as PaymentMethodEnum]
                  }
                  hotelId={hotel}
                />
              ))}
            </div>
          </section>
          <section className={styles.section}>
            <Caption>
              {intl.formatMessage<React.ReactNode>(
                {
                  id: "By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.",
                },
                {
                  termsAndConditionsLink: (str) => (
                    <Link
                      className={styles.link}
                      variant="underscored"
                      href={bookingTermsAndConditions[lang]}
                      target="_blank"
                    >
                      {str}
                    </Link>
                  ),
                  privacyPolicyLink: (str) => (
                    <Link
                      className={styles.link}
                      variant="underscored"
                      href={privacyPolicy[lang]}
                      target="_blank"
                    >
                      {str}
                    </Link>
                  ),
                }
              )}
            </Caption>
            <Checkbox name="termsAndConditions">
              <Caption>
                {intl.formatMessage({
                  id: "I accept the terms and conditions",
                })}
              </Caption>
            </Checkbox>
            <Checkbox name="smsConfirmation">
              <Caption>
                {intl.formatMessage({
                  id: "I would like to get my booking confirmation via sms",
                })}
              </Caption>
            </Checkbox>
          </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}
          oldPrice={priceChangeData.oldPrice}
          newPrice={priceChangeData.newPrice}
          currency={totalPrice.local.currency}
          onCancel={() => {
            const allSearchParams = searchParams.size
              ? `?${searchParams.toString()}`
              : ""
            router.push(`${selectRate(lang)}${allSearchParams}`)
          }}
          onAccept={() =>
            priceChange.mutate({ confirmationNumber: bookingNumber })
          }
        />
      ) : null}
    </>
  )
}
