"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 { Typography } from "@scandic-hotels/design-system/Typography"

import { PaymentMethodEnum } from "@/constants/booking"
import { guaranteeCallback } from "@/constants/routes/hotelReservation"
import { env } from "@/env/client"
import { trpc } from "@/lib/trpc/client"
import {
  AncillaryStepEnum,
  type BreakfastData,
  useAddAncillaryStore,
} from "@/stores/my-stay/add-ancillary-flow"

import Image from "@/components/Image"
import LoadingSpinner from "@/components/LoadingSpinner"
import Modal from "@/components/Modal"
import Divider from "@/components/TempDesignSystem/Divider"
import { toast } from "@/components/TempDesignSystem/Toasts"
import { useGuaranteeBooking } from "@/hooks/booking/useGuaranteeBooking"
import useLang from "@/hooks/useLang"
import { formatPrice } from "@/utils/numberFormatting"

import {
  clearAncillarySessionData,
  generateDeliveryOptions,
  getAncillarySessionData,
  setAncillarySessionData,
} from "../../utils"
import { type AncillaryFormData, ancillaryFormSchema } from "../schema"
import ActionButtons from "./ActionButtons"
import PriceDetails from "./PriceDetails"
import Steps from "./Steps"

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

import type {
  AddAncillaryFlowModalProps,
  Packages,
} from "@/types/components/myPages/myStay/ancillaries"
import { BreakfastPackageEnum } from "@/types/enums/breakfast"

export default function AddAncillaryFlowModal({
  booking,
  packages,
  user,
  savedCreditCards,
  refId,
}: 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 guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang)}`
  const deliveryTimeOptions = generateDeliveryOptions()

  const defaultDeliveryTime = deliveryTimeOptions[0].value

  const formMethods = useForm<AncillaryFormData>({
    defaultValues: {
      quantityWithPoints: null,
      quantityWithCard: user ? null : 1,
      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: "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({
    onSuccess: (data, variables) => {
      if (data) {
        clearAncillarySessionData()
        closeModal()
        utils.booking.confirmation.invalidate({
          confirmationNumber: variables.confirmationNumber,
        })

        toast.success(
          intl.formatMessage(
            { id: "{ancillary} added to your booking!" },
            { ancillary: selectedAncillary?.title }
          )
        )
        router.refresh()
      } else {
        toast.error(ancillaryErrorMessage)
      }
    },
    onError: () => {
      toast.error(ancillaryErrorMessage)
    },
  })

  const { guaranteeBooking, isLoading } = useGuaranteeBooking({
    confirmationNumber: booking.confirmationNumber,
  })

  const onSubmit = (data: AncillaryFormData) => {
    if (!data.termsAndConditions) {
      formMethods.setError("termsAndConditions", {
        message: "You must accept the terms",
      })
      return
    }

    setAncillarySessionData({
      formData: data,
      selectedAncillary,
    })
    if (booking.guaranteeInfo) {
      const packagesToAdd = []
      if (selectedAncillary?.id && data.quantityWithCard) {
        if (!isBreakfast) {
          packagesToAdd.push({
            code: selectedAncillary.id,
            quantity: data.quantityWithCard,
            comment: data.optionalText || undefined,
          })
        } else {
          if (!breakfastData) {
            toast.error(
              intl.formatMessage({
                id: "Something went wrong!",
              })
            )
            return
          }

          packagesToAdd.push({
            code: BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
            quantity: breakfastData.nrOfAdults,
            comment: data.optionalText || undefined,
          })
          packagesToAdd.push({
            code: BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
            quantity: breakfastData.nrOfPayingChildren,
            comment: data.optionalText || undefined,
          })
          packagesToAdd.push({
            code: BreakfastPackageEnum.FREE_CHILD_BREAKFAST,
            quantity: breakfastData.nrOfFreeChildren,
            comment: data.optionalText || undefined,
          })
        }
      }

      if (selectedAncillary?.loyaltyCode && data.quantityWithPoints) {
        packagesToAdd.push({
          code: selectedAncillary.loyaltyCode,
          quantity: data.quantityWithPoints,
          comment: data.optionalText || undefined,
        })
      }

      addAncillary.mutate({
        confirmationNumber: booking.confirmationNumber,
        ancillaryComment: data.optionalText,
        ancillaryDeliveryTime: selectedAncillary?.requiresDeliveryTime
          ? data.deliveryTime
          : undefined,
        packages: packagesToAdd,
        language: lang,
      })
    } else {
      const savedCreditCard = savedCreditCards?.find(
        (card) => card.id === data.paymentMethod
      )
      if (booking.confirmationNumber) {
        const card = savedCreditCard
          ? {
            alias: savedCreditCard.alias,
            expiryDate: savedCreditCard.expirationDate,
            cardType: savedCreditCard.cardType,
          }
          : undefined
        guaranteeBooking.mutate({
          confirmationNumber: booking.confirmationNumber,
          language: lang,
          ...(card && { card }),
          success: `${guaranteeRedirectUrl}?status=success&RefId=${encodeURIComponent(refId)}&ancillary=1`,
          error: `${guaranteeRedirectUrl}?status=error&RefId=${encodeURIComponent(refId)}&ancillary=1`,
          cancel: `${guaranteeRedirectUrl}?status=cancel&RefId=${encodeURIComponent(refId)}&ancillary=1`,
        })
      } else {
        toast.error(
          intl.formatMessage({
            id: "Something went wrong!",
          })
        )
      }
    }
  }

  useEffect(() => {
    const errorCode = searchParams.get("errorCode")
    const ancillary = searchParams.get("ancillary")
    if ((errorCode && ancillary) || errorCode === "AncillaryFailed") {
      const queryParams = new URLSearchParams(searchParams.toString())
      if (ancillary) {
        queryParams.delete("ancillary")
      }
      queryParams.delete("errorCode")
      const savedData = getAncillarySessionData()
      if (savedData?.formData) {
        formMethods.reset(savedData.formData)
      }
      router.replace(`${pathname}?${queryParams.toString()}`)
    }
  }, [searchParams, pathname, formMethods, router])

  useEffect(() => {
    setBreakfastData(
      calculateBreakfastData(
        isBreakfast,
        packages,
        booking.adults,
        booking.childrenAges
      )
    )
  }, [
    booking.adults,
    booking.childrenAges,
    isBreakfast,
    packages,
    setBreakfastData,
  ])

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

  const modalTitle =
    currentStep === AncillaryStepEnum.selectAncillary
      ? intl.formatMessage({ id: "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 && (
                <>
                  <div className={styles.imageContainer}>
                    <Image
                      className={styles.image}
                      src={selectedAncillary.imageUrl}
                      alt={selectedAncillary.title}
                      fill
                    />
                  </div>
                  {currentStep !== AncillaryStepEnum.confirmation && (
                    <div className={styles.contentContainer}>
                      <div className={styles.price}>
                        <Typography variant="Body/Paragraph/mdBold">
                          <p>
                            {isBreakfast ? (
                              <BreakfastPriceList />
                            ) : (
                              formatPrice(
                                intl,
                                selectedAncillary.price.total,
                                selectedAncillary.price.currency
                              )
                            )}
                          </p>
                        </Typography>
                        {selectedAncillary.points && (
                          <div className={styles.divider}>
                            <Divider variant="vertical" color="subtle" />
                            <Typography variant="Body/Paragraph/mdBold">
                              <p>
                                {intl.formatMessage(
                                  { id: "{value} points" },
                                  {
                                    value: 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} />
            </div>
          </form>
          {currentStep === AncillaryStepEnum.selectAncillary ? null : (
            <div
              className={
                currentStep === AncillaryStepEnum.confirmation
                  ? styles.confirmStep
                  : styles.actionButtons
              }
            >
              <PriceDetails isPriceDetailsOpen={isPriceDetailsOpen} />
              <ActionButtons
                isPriceDetailsOpen={isPriceDetailsOpen}
                togglePriceDetails={togglePriceDetails}
                isSubmitting={addAncillary.isPending || isLoading}
              />
            </div>
          )}
        </FormProvider>
      </div>
    </Modal>
  )
}

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

  if (!breakfastData) {
    return intl.formatMessage({
      id: "Can not show breakfast prices.",
    })
  }

  return (
    <div>
      <div className={styles.breakfastPriceList}>
        <Typography variant="Body/Paragraph/mdBold">
          <span>{`${breakfastData.priceAdult} ${breakfastData.currency} / ${intl.formatMessage({ id: "Adult" })}`}</span>
        </Typography>

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

            <Typography variant="Body/Paragraph/mdBold">
              <span>{`${breakfastData.priceChild} ${breakfastData.currency} / ${intl.formatMessage({ id: "Years" })} 4-12`}</span>
            </Typography>
          </>
        )}

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

            <Typography variant="Body/Paragraph/mdBold">
              <span>{`${intl.formatMessage({ id: "Free" })} / ${intl.formatMessage({ id: "Under {age} years" }, { age: 4 })}`}</span>
            </Typography>
          </>
        )}
      </div>
    </div>
  )
}

/**
 * This function calculates some breakfast data in the store.
 * It is used in various places in the add flow, but only needs
 * to be calculated once.
 */
function calculateBreakfastData(
  isBreakfast: boolean,
  packages: Packages | null,
  nrOfAdults: number,
  childrenAges: number[]
): BreakfastData | null {
  if (!isBreakfast) {
    return null
  }

  const [nrOfPayingChildren, nrOfFreeChildren] = childrenAges.reduce(
    (acc, curr) => (curr >= 4 ? [acc[0] + 1, acc[1]] : [acc[0], acc[1] + 1]),
    [0, 0]
  )

  const priceAdult = packages?.find(
    (p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
  )?.localPrice.price
  const priceChild = packages?.find(
    (p) => p.code === BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST
  )?.localPrice.price
  const currency = packages?.find(
    (p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
  )?.localPrice.currency

  if (
    typeof priceAdult !== "number" ||
    typeof priceChild !== "number" ||
    typeof currency !== "string"
  ) {
    return null
  } else {
    return {
      nrOfAdults,
      nrOfPayingChildren,
      nrOfFreeChildren,
      priceAdult,
      priceChild,
      currency,
    }
  }
}
