"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 { dt } from "@scandic-hotels/common/dt"
import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
import { toast } from "@scandic-hotels/design-system/Toast"
import { trpc } from "@scandic-hotels/trpc/client"

import { 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 AddAncillaryFlow from "./Modal"
import { type AncillaryFormData, ancillaryFormSchema } from "./schema"
import {
  buildBreakfastPackages,
  calculateBreakfastData,
  getErrorMessage,
  getGuaranteeCallback,
} 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 {
    selectedAncillary,
    closeModal,
    breakfastData,
    setBreakfastData,
    isBreakfast,
  } = useAddAncillaryStore((state) => ({
    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 [errorMessage, setErrorMessage] =
    useState<AncillaryErrorMessage | null>(null)
  const guaranteeRedirectUrl = getGuaranteeCallback(lang, pathname)
  const deliveryTimeOptions = generateDeliveryOptions()

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

  const formMethods = useForm({
    defaultValues: {
      quantityWithPoints: null,
      quantityWithCard:
        !user || hasInsufficientPoints || !selectedAncillary?.requiresQuantity
          ? 1
          : null,
      deliveryTime:
        booking.ancillary?.deliveryTime ?? deliveryTimeOptions[0].value,
      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 }
  )

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

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

  async function handleAncillarySubmission(
    data: AncillaryFormData,
    packages: {
      code: string
      quantity: number
      comment: string | undefined
    }[]
  ) {
    await addAncillary.mutateAsync(
      {
        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)
            closeModal()
          }
        },
        onError: () => {
          trackAncillaryFailed(
            packages,
            data.deliveryTime,
            selectedAncillary,
            breakfastData
          )
          toast.error(ancillaryErrorMessage)
          closeModal()
        },
      }
    )
  }

  async 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
      await guaranteeBooking.mutateAsync({
        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 = async (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

    if (shouldSkipGuarantee) {
      await handleAncillarySubmission(data, packagesToAdd)
    } else {
      await 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>
    )
  }

  return (
    <FormProvider {...formMethods}>
      <form
        onSubmit={formMethods.handleSubmit(onSubmit)}
        className={styles.form}
        id="add-ancillary-form-id"
      >
        <AddAncillaryFlow
          error={errorMessage}
          savedCreditCards={savedCreditCards}
          user={user}
        />
      </form>
    </FormProvider>
  )
}
