"use client"

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

import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import { guaranteeCallback } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { dt } from "@scandic-hotels/common/dt"
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
import { Divider } from "@scandic-hotels/design-system/Divider"
import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
import Modal from "@scandic-hotels/design-system/Modal"
import { toast } from "@scandic-hotels/design-system/Toast"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { trpc } from "@scandic-hotels/trpc/client"

import { isWebview } from "@/constants/routes/webviews"
import { env } from "@/env/client"
import {
  AncillaryStepEnum,
  useAddAncillaryStore,
} from "@/stores/my-stay/add-ancillary-flow"

import {
  buildAncillaryPackages,
  clearAncillarySessionData,
  generateDeliveryOptions,
  getAncillarySessionData,
  setAncillarySessionData,
} from "@/components/HotelReservation/MyStay/utils/ancillaries"
import { useGuaranteeBooking } from "@/hooks/booking/useGuaranteeBooking"
import useLang from "@/hooks/useLang"
import {
  trackAncillaryFailed,
  trackAncillarySuccess,
  trackGlaAncillaryAttempt,
} from "@/utils/tracking/myStay"

import { isAncillaryError } from "../../../utils"
import { type AncillaryFormData, ancillaryFormSchema } from "../schema"
import ActionButtons from "./ActionButtons"
import PriceDetails from "./PriceDetails"
import Steps from "./Steps"
import {
  buildBreakfastPackages,
  calculateBreakfastData,
  getErrorMessage,
} from "./utils"

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

import type {
  AddAncillaryFlowModalProps,
  AncillaryErrorMessage,
  AncillaryItem,
} from "@/types/components/myPages/myStay/ancillaries"

export default function AddAncillaryFlowModal({
  booking,
  packages,
  user,
  savedCreditCards,
}: AddAncillaryFlowModalProps) {
  const {
    currentStep,
    selectedAncillary,
    closeModal,
    breakfastData,
    setBreakfastData,
    isBreakfast,
  } = useAddAncillaryStore((state) => ({
    currentStep: state.currentStep,
    selectedAncillary: state.selectedAncillary,
    closeModal: state.closeModal,
    breakfastData: state.breakfastData,
    setBreakfastData: state.setBreakfastData,
    isBreakfast: state.isBreakfast,
  }))
  const intl = useIntl()
  const lang = useLang()
  const router = useRouter()
  const searchParams = useSearchParams()
  const pathname = usePathname()

  const [isPriceDetailsOpen, setIsPriceDetailsOpen] = useState(false)
  const [errorMessage, setErrorMessage] =
    useState<AncillaryErrorMessage | null>(null)
  const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang, isWebview(pathname))}`
  const deliveryTimeOptions = generateDeliveryOptions()

  const defaultDeliveryTime = deliveryTimeOptions[0].value

  const hasInsufficientPoints =
    (user?.membership?.currentPoints ?? 0) < (selectedAncillary?.points ?? 0)

  const formMethods = useForm({
    defaultValues: {
      quantityWithPoints: null,
      quantityWithCard:
        !user || hasInsufficientPoints || isBreakfast ? 1 : null,
      deliveryTime: booking.ancillary?.deliveryTime ?? defaultDeliveryTime,
      optionalText: "",
      termsAndConditions: false,
      paymentMethod: booking.guaranteeInfo
        ? PaymentMethodEnum.card
        : savedCreditCards?.length
          ? savedCreditCards[0].id
          : PaymentMethodEnum.card,
    },
    mode: "onChange",
    reValidateMode: "onChange",
    resolver: zodResolver(ancillaryFormSchema),
  })

  const ancillaryErrorMessage = intl.formatMessage(
    {
      id: "addAncillaryFlowModal.errorMessage.ancillary",
      defaultMessage:
        "Something went wrong. {ancillary} could not be added to your booking!",
    },
    { ancillary: selectedAncillary?.title }
  )

  function togglePriceDetails() {
    setIsPriceDetailsOpen((isOpen) => !isOpen)
  }

  const utils = trpc.useUtils()
  const addAncillary = trpc.booking.packages.useMutation()

  const { guaranteeBooking, isLoading, handleGuaranteeError } =
    useGuaranteeBooking(booking.refId, true, booking.hotelId)

  function handleAncillarySubmission(
    data: AncillaryFormData,
    packages: {
      code: string
      quantity: number
      comment: string | undefined
    }[]
  ) {
    addAncillary.mutate(
      {
        refId: booking.refId,
        ancillaryComment: data.optionalText,
        ancillaryDeliveryTime: selectedAncillary?.requiresDeliveryTime
          ? data.deliveryTime
          : undefined,
        packages: packages,
        language: lang,
      },
      {
        onSuccess: (result) => {
          if (result) {
            trackAncillarySuccess(
              booking.confirmationNumber,
              packages,
              data.deliveryTime,
              "ancillary",
              selectedAncillary,
              breakfastData,
              booking.guaranteeInfo?.cardType,
              booking.roomTypeCode
            )
            toast.success(
              intl.formatMessage(
                {
                  id: "addAncillaryFlowModal.ancillaryAdded",
                  defaultMessage: "{ancillary} added to your booking!",
                },
                { ancillary: selectedAncillary?.title }
              )
            )
            clearAncillarySessionData()
            closeModal()
            utils.booking.get.invalidate({
              refId: booking.refId,
            })
            router.refresh()
          } else {
            trackAncillaryFailed(
              packages,
              data.deliveryTime,
              selectedAncillary,
              breakfastData
            )
            toast.error(ancillaryErrorMessage)
          }
        },
        onError: () => {
          trackAncillaryFailed(
            packages,
            data.deliveryTime,
            selectedAncillary,
            breakfastData
          )
          toast.error(ancillaryErrorMessage)
        },
      }
    )
  }

  function handleGuaranteePayment(
    data: AncillaryFormData,
    packages: AncillaryItem[]
  ) {
    const savedCreditCard = savedCreditCards?.find(
      (card) => card.id === data.paymentMethod
    )
    trackGlaAncillaryAttempt(
      savedCreditCard,
      packages,
      selectedAncillary,
      data.deliveryTime,
      breakfastData
    )
    if (booking.refId) {
      const card = savedCreditCard
        ? {
            alias: savedCreditCard.alias,
            expiryDate: savedCreditCard.expirationDate,
            cardType: savedCreditCard.cardType,
          }
        : undefined
      guaranteeBooking.mutate({
        refId: booking.refId,
        language: lang,
        ...(card && { card }),
        success: `${guaranteeRedirectUrl}?status=success&ancillary=1&RefId=${encodeURIComponent(booking.refId)}`,
        error: `${guaranteeRedirectUrl}?status=error&ancillary=1&RefId=${encodeURIComponent(booking.refId)}`,
        cancel: `${guaranteeRedirectUrl}?status=cancel&ancillary=1&RefId=${encodeURIComponent(booking.refId)}`,
      })
    } else {
      handleGuaranteeError("No confirmation number")
    }
  }

  const onSubmit = (data: AncillaryFormData) => {
    const packagesToAdd = !isBreakfast
      ? buildAncillaryPackages(data, selectedAncillary)
      : breakfastData
        ? buildBreakfastPackages(data, breakfastData)
        : []

    if (isBreakfast && !breakfastData) {
      toast.error(
        intl.formatMessage({
          id: "errorMessage.somethingWentWrong",
          defaultMessage: "Something went wrong!",
        })
      )
      return
    }
    setAncillarySessionData({
      formData: data,
      selectedAncillary,
      packages: packagesToAdd,
      isBreakfast,
      breakfastData,
    })
    const shouldSkipGuarantee =
      booking.guaranteeInfo || (data.quantityWithCard ?? 0) <= 0

    if (shouldSkipGuarantee) {
      handleAncillarySubmission(data, packagesToAdd)
    } else {
      handleGuaranteePayment(data, packagesToAdd)
    }
  }

  useEffect(() => {
    if (isAncillaryError(searchParams)) {
      const errorCode = searchParams.get("errorCode")
      const queryParams = new URLSearchParams(searchParams.toString())
      const savedData = getAncillarySessionData()
      if (savedData?.formData) {
        const updatedFormData = {
          ...savedData.formData,
          paymentMethod: booking?.guaranteeInfo
            ? PaymentMethodEnum.card
            : savedData.formData.paymentMethod,
        }
        formMethods.reset(updatedFormData)
      }

      setErrorMessage(getErrorMessage(intl, errorCode))
      queryParams.delete("ancillary")
      queryParams.delete("errorCode")
      router.replace(`${pathname}?${queryParams.toString()}`)
    }
  }, [searchParams, pathname, formMethods, router, booking.guaranteeInfo, intl])

  useEffect(() => {
    setBreakfastData(
      calculateBreakfastData(
        isBreakfast,
        packages,
        booking.adults,
        booking.childrenAges,
        dt(booking.checkOutDate)
          .startOf("day")
          .diff(dt(booking.checkInDate).startOf("day"), "days")
      )
    )
  }, [
    booking.adults,
    booking.checkInDate,
    booking.checkOutDate,
    booking.childrenAges,
    isBreakfast,
    packages,
    setBreakfastData,
  ])

  if (isLoading) {
    return (
      <div>
        <LoadingSpinner />
      </div>
    )
  }

  const modalTitle =
    currentStep === AncillaryStepEnum.selectAncillary
      ? intl.formatMessage({
          id: "ancillaries.upgradeYourStay",
          defaultMessage: "Upgrade your stay",
        })
      : selectedAncillary?.title
  return (
    <Modal isOpen={true} onToggle={closeModal} title={modalTitle}>
      <div
        className={`${styles.modalWrapper} ${currentStep === AncillaryStepEnum.selectAncillary ? styles.selectAncillarycontainer : ""}`}
      >
        <FormProvider {...formMethods}>
          <form
            onSubmit={formMethods.handleSubmit(onSubmit)}
            className={styles.form}
            id="add-ancillary-form-id"
          >
            <div className={styles.modalScrollable}>
              {selectedAncillary && (
                <>
                  {currentStep !== AncillaryStepEnum.confirmation && (
                    <div className={styles.contentContainer}>
                      <div className={styles.price}>
                        <Typography variant="Body/Paragraph/mdBold">
                          {isBreakfast ? (
                            <BreakfastPriceList />
                          ) : (
                            <p>
                              {formatPrice(
                                intl,
                                selectedAncillary.price.total,
                                selectedAncillary.price.currency
                              )}
                            </p>
                          )}
                        </Typography>
                        {selectedAncillary.points && (
                          <div className={styles.pointsDivider}>
                            <Divider variant="vertical" />
                            <Typography variant="Body/Paragraph/mdBold">
                              <p>
                                {intl.formatMessage(
                                  {
                                    id: "common.numberOfPoints",
                                    defaultMessage:
                                      "{points, plural, one {# point} other {# points}}",
                                  },
                                  {
                                    points: selectedAncillary.points,
                                  }
                                )}
                              </p>
                            </Typography>
                          </div>
                        )}
                      </div>
                      <div className={styles.description}>
                        {selectedAncillary.description && (
                          <Typography variant="Body/Paragraph/mdRegular">
                            <p
                              dangerouslySetInnerHTML={{
                                __html: selectedAncillary.description,
                              }}
                            ></p>
                          </Typography>
                        )}
                      </div>
                    </div>
                  )}
                </>
              )}
              <Steps
                user={user}
                savedCreditCards={savedCreditCards}
                error={errorMessage}
              />
              {currentStep === AncillaryStepEnum.selectAncillary ? null : (
                <div
                  className={
                    currentStep === AncillaryStepEnum.confirmation ||
                    isBreakfast
                      ? styles.confirmStep
                      : ""
                  }
                >
                  <PriceDetails isPriceDetailsOpen={isPriceDetailsOpen} />
                  <ActionButtons
                    isPriceDetailsOpen={isPriceDetailsOpen}
                    togglePriceDetails={togglePriceDetails}
                    isSubmitting={addAncillary.isPending || isLoading}
                  />
                </div>
              )}
            </div>
          </form>
        </FormProvider>
      </div>
    </Modal>
  )
}

function BreakfastPriceList() {
  const intl = useIntl()
  const breakfastData = useAddAncillaryStore((state) => state.breakfastData)

  if (!breakfastData) {
    return intl.formatMessage({
      id: "ancillaries.unableToDisplayBreakfastPrices",
      defaultMessage: "Unable to display breakfast prices.",
    })
  }

  return (
    <div>
      <div className={styles.breakfastPriceList}>
        <Typography variant="Body/Paragraph/mdBold">
          <span>
            {intl.formatMessage(
              {
                id: "addAncillaryFlowModal.pricePerNightPerAdult",
                defaultMessage: "{price}/night per adult",
              },
              {
                price: `${breakfastData.priceAdult} ${breakfastData.currency}`,
              }
            )}
          </span>
        </Typography>

        {breakfastData.nrOfPayingChildren > 0 && (
          <>
            <div className={styles.divider}>
              <Divider variant="vertical" color="Border/Divider/Subtle" />
            </div>

            <Typography variant="Body/Paragraph/mdBold">
              <span>
                {intl.formatMessage(
                  {
                    id: "addAncillaryFlowModal.pricePerNightPerKids",
                    defaultMessage: "{price}/night for kids (ages 4–12)",
                  },
                  {
                    price: `${breakfastData.priceChild} ${breakfastData.currency}`,
                  }
                )}
              </span>
            </Typography>
          </>
        )}

        {breakfastData.nrOfFreeChildren > 0 && (
          <>
            <div className={styles.divider}>
              <Divider variant="vertical" color="Border/Divider/Subtle" />
            </div>

            <Typography variant="Body/Paragraph/mdBold">
              <span>
                {intl.formatMessage({
                  id: "addAncillaryFlowModal.freeBreakfastForKids",
                  defaultMessage: "Free for kids (under 4)",
                })}
              </span>
            </Typography>
          </>
        )}
      </div>
    </div>
  )
}
