"use client"

import { zodResolver } from "@hookform/resolvers/zod"
import { useRouter, useSearchParams } from "next/navigation"
import { useEffect, useState } from "react"
import { Label as AriaLabel } from "react-aria-components"
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 { 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 { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus"
import useLang from "@/hooks/useLang"

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

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

import { RoomPackageCodeEnum } from "@/types/components/hotelReservation/selectRate/roomFilter"
import { PaymentProps } from "@/types/components/hotelReservation/selectRate/section"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"

const maxRetries = 40
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 Payment({
  roomPrice,
  otherPaymentOptions,
  savedCreditCards,
  mustBeGuaranteed,
}: PaymentProps) {
  const router = useRouter()
  const lang = useLang()
  const intl = useIntl()
  const queryParams = useSearchParams()
  const { userData, roomData, setIsSubmittingDisabled } = useEnterDetailsStore(
    (state) => ({
      userData: state.userData,
      roomData: state.roomData,
      setIsSubmittingDisabled: state.setIsSubmittingDisabled,
    })
  )

  const {
    firstName,
    lastName,
    email,
    phoneNumber,
    countryCode,
    breakfast,
    bedType,
    membershipNo,
  } = userData
  const { toDate, fromDate, rooms: rooms, hotel } = roomData

  const [confirmationNumber, setConfirmationNumber] = useState<string>("")
  const [availablePaymentOptions, setAvailablePaymentOptions] =
    useState(otherPaymentOptions)

  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?.confirmationNumber) {
        setConfirmationNumber(result.confirmationNumber)
      } else {
        // TODO: add proper error message
        toast.error("Failed to create booking")
      }
    },
    onError: (error) => {
      console.error("Error", error)
      // TODO: add proper error message
      toast.error("Failed to create booking")
    },
  })

  const bookingStatus = useHandleBookingStatus(
    confirmationNumber,
    BookingStatusEnum.PaymentRegistered,
    maxRetries,
    retryInterval
  )

  useEffect(() => {
    if (window.ApplePaySession) {
      setAvailablePaymentOptions(otherPaymentOptions)
    } else {
      setAvailablePaymentOptions(
        otherPaymentOptions.filter(
          (option) => option !== PaymentMethodEnum.applePay
        )
      )
    }
  }, [otherPaymentOptions, setAvailablePaymentOptions])

  useEffect(() => {
    if (bookingStatus?.data?.paymentUrl) {
      router.push(bookingStatus.data.paymentUrl)
    }
  }, [bookingStatus, router])

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

  function handleSubmit(data: PaymentFormData) {
    const allQueryParams =
      queryParams.size > 0 ? `?${queryParams.toString()}` : ""

    // 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
    )

    initiateBooking.mutate({
      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: room.rateCode,
        roomTypeCode: bedType!.roomTypeCode, // A selection has been made in order to get to this step.
        guest: {
          title: "",
          firstName,
          lastName,
          email,
          phoneNumber,
          countryCode,
          membershipNumber: membershipNo,
        },
        packages: {
          breakfast: breakfast !== BreakfastPackageEnum.NO_BREAKFAST,
          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: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/success`,
        error: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/error${allQueryParams}`,
        cancel: `${env.NEXT_PUBLIC_PAYMENT_CALLBACK_URL}/${lang}/cancel${allQueryParams}`,
      },
    })
  }

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

  const guaranteeing = intl.formatMessage({ id: "guaranteeing" })
  const paying = intl.formatMessage({ id: "paying" })
  const paymentVerb = mustBeGuaranteed ? guaranteeing : paying

  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}
                />
              ))}
            </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" })}
            />
            {availablePaymentOptions.map((paymentMethod) => (
              <PaymentOption
                key={paymentMethod}
                name="paymentMethod"
                value={paymentMethod}
                label={
                  PAYMENT_METHOD_TITLES[paymentMethod as PaymentMethodEnum]
                }
              />
            ))}
          </div>
        </section>
        <section className={styles.section}>
          <Checkbox name="smsConfirmation">
            <Caption>
              {intl.formatMessage({
                id: "I would like to get my booking confirmation via sms",
              })}
            </Caption>
          </Checkbox>

          <AriaLabel className={styles.terms}>
            <Checkbox name="termsAndConditions" />
            <Caption>
              {intl.formatMessage<React.ReactNode>(
                {
                  id: "booking.terms",
                },
                {
                  paymentVerb,
                  termsLink: (str) => (
                    <Link
                      className={styles.link}
                      variant="underscored"
                      href={bookingTermsAndConditions[lang]}
                      target="_blank"
                    >
                      {str}
                    </Link>
                  ),
                  privacyLink: (str) => (
                    <Link
                      className={styles.link}
                      variant="underscored"
                      href={privacyPolicy[lang]}
                      target="_blank"
                    >
                      {str}
                    </Link>
                  ),
                }
              )}
            </Caption>
          </AriaLabel>
        </section>
        <div className={styles.submitButton}>
          <Button
            type="submit"
            disabled={
              !methods.formState.isValid || methods.formState.isSubmitting
            }
          >
            {intl.formatMessage({ id: "Complete booking" })}
          </Button>
        </div>
      </form>
    </FormProvider>
  )
}
