"use client"

import { usePathname, useSearchParams } from "next/navigation"
import { useCallback, useEffect, useRef, useState } from "react"
import { useIntl } from "react-intl"

import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { useSessionId } from "@scandic-hotels/common/hooks/useSessionId"
import useStickyPosition from "@scandic-hotels/common/hooks/useStickyPosition"
import { Alert } from "@scandic-hotels/design-system/Alert"
import { trackNoAvailability } from "@scandic-hotels/tracking/NoAvailabilityTracking"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"

import useLang from "../../../../hooks/useLang"
import { useEnterDetailsStore } from "../../../../stores/enter-details"
import { mapPackageToLabel } from "../../../../utils/getRoomFeatureDescription"

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

function useBookingErrorAlert() {
  const updateSearchParams = useEnterDetailsStore(
    (state) => state.actions.updateSeachParamString
  )

  const intl = useIntl()
  const lang = useLang()
  const searchParams = useSearchParams()
  const pathname = usePathname()

  const errorCode = searchParams.get("errorCode")
  const errorMessage = getErrorMessage(errorCode)
  const severityLevel =
    errorCode === BookingErrorCodeEnum.TransactionCancelled
      ? AlertTypeEnum.Warning
      : AlertTypeEnum.Alarm

  const [showAlert, setShowAlert] = useState(!!errorCode)

  const selectRateReturnUrl = getSelectRateReturnUrl()

  useEffect(() => {
    setShowAlert(!!errorCode)
  }, [errorCode])

  function getErrorMessage(errorCode: string | null) {
    switch (errorCode) {
      case BookingErrorCodeEnum.TransactionCancelled:
        return intl.formatMessage({
          defaultMessage: "You have now cancelled your payment.",
        })
      case BookingErrorCodeEnum.AvailabilityError:
      case BookingErrorCodeEnum.NoAvailabilityForRateAndRoomType:
        return intl.formatMessage({
          defaultMessage:
            "Unfortunately, one of the rooms you selected is sold out. Please choose another room to proceed.",
        })
      default:
        return intl.formatMessage({
          defaultMessage:
            "We had an issue processing your booking. Please try again. No charges have been made.",
        })
    }
  }

  function discardAlert() {
    setShowAlert(false)
    const queryParams = new URLSearchParams(searchParams.toString())
    queryParams.delete("errorCode")
    updateSearchParams(queryParams.toString())

    window.history.replaceState({}, "", `${pathname}?${queryParams.toString()}`)
  }

  function getSelectRateReturnUrl() {
    const queryParams = new URLSearchParams(searchParams.toString())
    queryParams.delete("errorCode")
    return `${selectRate(lang)}?${queryParams.toString()}`
  }

  return {
    showAlert,
    errorCode,
    errorMessage,
    severityLevel,
    discardAlert,
    setShowAlert,
    selectRateReturnUrl,
  }
}

interface BookingAlertProps {
  isVisible?: boolean
}

export default function BookingAlert({ isVisible = false }: BookingAlertProps) {
  const intl = useIntl()

  const {
    showAlert,
    errorCode,
    errorMessage,
    severityLevel,
    discardAlert,
    setShowAlert,
    selectRateReturnUrl,
  } = useBookingErrorAlert()

  const trackNoAvailabilityError = useNoAvailabilityTracking()

  const isAvailabilityError =
    errorCode === BookingErrorCodeEnum.AvailabilityError ||
    errorCode === BookingErrorCodeEnum.NoAvailabilityForRateAndRoomType

  const ref = useRef<HTMLDivElement>(null)
  const { getTopOffset } = useStickyPosition()

  useEffect(() => {
    if (isVisible) {
      setShowAlert(true)
    }
  }, [isVisible, setShowAlert])

  useEffect(() => {
    const el = ref.current

    if (showAlert && el) {
      window.scrollTo({
        top: el.offsetTop - getTopOffset(),
        behavior: "smooth",
      })
    }
  }, [showAlert, getTopOffset])

  useEffect(() => {
    if (showAlert && isAvailabilityError) {
      trackNoAvailabilityError()
    }
  }, [showAlert, isAvailabilityError, trackNoAvailabilityError])

  if (!showAlert) return null

  return (
    <div className={styles.wrapper} ref={ref}>
      <Alert
        type={severityLevel}
        variant="inline"
        text={errorMessage}
        close={discardAlert}
        link={
          isAvailabilityError
            ? {
                title: intl.formatMessage({
                  defaultMessage: "Change room",
                }),
                url: selectRateReturnUrl,
              }
            : undefined
        }
      />
    </div>
  )
}

function useNoAvailabilityTracking() {
  const { fromDate, toDate, hotelId, bookingCode, searchType, rooms } =
    useEnterDetailsStore((state) => state.booking)
  const lang = useLang()
  const sessionId = useSessionId()

  const specialRoomType = rooms
    ?.map((room) => {
      const packages = room.packages
        ?.map((pkg) => mapPackageToLabel(pkg))
        .join(",")
      return packages ?? ""
    })
    .join("|")

  const track = useCallback(
    () =>
      trackNoAvailability({
        specialRoomType,
        lang,
        searchTerm: hotelId,
        fromDate,
        toDate,
        hotelId,
        noOfRooms: rooms.length,
        searchType,
        bookingCode: bookingCode ?? "n/a",
        pageId: "details",
        pageName: "hotelreservation|details",
        pageType: "bookingenterdetailspage",
        siteSections: "hotelreservation|details",
        sessionId,
        domain: typeof window !== "undefined" ? window.location.host : "",
      }),
    [
      specialRoomType,
      lang,
      hotelId,
      fromDate,
      toDate,
      rooms.length,
      searchType,
      bookingCode,
      sessionId,
    ]
  )
  return track
}
