"use client"

import NextLink from "next/link"
import { type ReadonlyURLSearchParams, useSearchParams } from "next/navigation"
import { memo, useState } from "react"
import { useFocusWithin } from "react-aria"
import { useIntl } from "react-intl"

import {
  alternativeHotelsMap,
  selectHotelMap,
  selectRate,
} from "@scandic-hotels/common/constants/routes/hotelReservation"
import { getSingleDecimal } from "@scandic-hotels/common/utils/numberFormatting"
import { Divider } from "../Divider"
import { FacilityToIcon } from "../FacilityToIcon"
import HotelLogoIcon from "../Icons/Logos"
import ImageGallery, { GalleryImage } from "../ImageGallery"
import Link from "../OldDSLink"
import { Typography } from "../Typography"
import { HotelPointsRow } from "./HotelPointsRow"
import { NoPriceAvailableCard } from "./NoPriceAvailableCard"

import HotelChequeCard from "./HotelChequeCard"
import { HotelPriceCard } from "./HotelPriceCard"
import HotelVoucherCard from "./HotelVoucherCard"
import { hotelCardVariants } from "./variants"

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

import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { FacilityEnum } from "@scandic-hotels/common/constants/facilities"
import { HotelType } from "@scandic-hotels/common/constants/hotelType"
import type { Lang } from "@scandic-hotels/common/constants/language"
import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
import { BookingCodeChip } from "../BookingCodeChip"
import { FakeButton } from "../FakeButton"
import { TripAdvisorChip } from "../TripAdvisorChip"

type Price = {
  pricePerStay: number
  pricePerNight: number
  currency: string
}

export type HotelCardProps = {
  hotel: {
    id: string
    hotelType: HotelType
    name: string
    description?: string
    detailedFacilities: { name: string; id: FacilityEnum }[]
    address: {
      city: string
      streetAddress: string
    }
    ratings?: {
      tripAdvisor?: number
    }
  }
  prices:
    | {
        public?: {
          rateType: RateTypeEnum
          localPrice: Price
          requestedPrice?: Price
        }
        member?: {
          rateType: RateTypeEnum
          localPrice: Price
          requestedPrice?: Price
        }
        voucher?: {
          numberOfVouchers: number
          rateCode: string
          rateType: RateTypeEnum
        }
        bonusCheque?: {
          rateCode: string
          rateType: RateTypeEnum
          localPrice: {
            additionalPricePerStay: number
            currency: CurrencyEnum | null | undefined
            numberOfCheques: number
          }
          requestedPrice?: {
            additionalPricePerStay: number
            currency: CurrencyEnum | null | undefined
            numberOfCheques: number
          }
        }
        redemptions?: {
          rateCode: string
          hasEnoughPoints: boolean
          localPrice: {
            additionalPricePerStay: number
            pointsPerStay: number
            currency: CurrencyEnum | null | undefined
          }
        }[]
      }
    | undefined
  images: GalleryImage[]
  distanceToCityCenter: number
  isUserLoggedIn: boolean
  type?: "mapListing" | "pageListing"
  state?: "default" | "active"
  bookingCode?: string | null
  isAlternative?: boolean
  isPartnerBrand: boolean
  pointsCurrency?: CurrencyEnum
  fullPrice: boolean
  isCampaignWithBookingCode: boolean
  lang: Lang

  belowInfoSlot: React.ReactNode

  onHover: () => void
  onHoverEnd: () => void
  onFocusIn: () => void
  onFocusOut: () => void
  onAddressClick: () => void
}

export const HotelCardComponent = memo(
  ({
    prices,
    hotel,
    distanceToCityCenter,
    isUserLoggedIn,
    state = "default",
    type = "pageListing",
    bookingCode = "",
    isAlternative,
    isPartnerBrand,
    pointsCurrency,
    images,
    lang,
    belowInfoSlot,
    fullPrice,
    isCampaignWithBookingCode,
    onAddressClick,
    onHover,
    onHoverEnd,
    onFocusIn,
    onFocusOut,
  }: HotelCardProps) => {
    const searchParams = useSearchParams()
    const [isFocusWithin, setIsFocusWithin] = useState(false)
    const [isPricesHovered, setIsPricesHovered] = useState(false)
    const { focusWithinProps } = useFocusWithin({
      onFocusWithin: onFocusIn,
      onBlurWithin: onFocusOut,
      onFocusWithinChange: (isFocusWithin) => {
        setIsFocusWithin(isFocusWithin)
      },
    })

    const intl = useIntl()

    const amenities = hotel.detailedFacilities.slice(0, 5)
    const classNames = hotelCardVariants({
      type,
      state,
    })

    const mapUrl = isAlternative
      ? alternativeHotelsMap(lang)
      : selectHotelMap(lang)
    const handleAddressClick = (event: React.MouseEvent) => {
      event.preventDefault()
      onAddressClick()
    }

    const addressStr = `${hotel.address.streetAddress}, ${hotel.address.city}`

    const hasInsufficientPoints = !prices?.redemptions?.some(
      (r) => r.hasEnoughPoints
    )
    const notEnoughPointsLabel = intl.formatMessage({
      id: "booking.notEnoughPoints",
      defaultMessage: "Not enough points",
    })

    const isDisabled = prices?.redemptions?.length && hasInsufficientPoints
    const isCampaign =
      prices?.public?.rateType === RateTypeEnum.PublicPromotion ||
      prices?.member?.rateType === RateTypeEnum.PublicPromotion
    const showBookingCodeChip = bookingCode || isCampaign

    function onMouseEnter() {
      if (!isFocusWithin) {
        onHover()
      }
    }

    function onMouseLeave() {
      if (!isFocusWithin) {
        onHoverEnd()
      }
    }

    return (
      <article
        {...focusWithinProps}
        className={classNames}
        onMouseEnter={onMouseEnter}
        onMouseLeave={onMouseLeave}
      >
        <div>
          <div className={styles.imageContainer}>
            <ImageGallery
              title={hotel.name}
              images={images}
              fill
              sizes="(min-width: 768px) calc(100vw - 340px), (min-width: 1367px) 33vw, 100vw"
            />
            {hotel.ratings?.tripAdvisor && (
              <TripAdvisorChip rating={hotel.ratings.tripAdvisor} />
            )}
          </div>
        </div>
        <div className={styles.hotelContent}>
          <div className={styles.hotelInformation}>
            <div className={styles.titleContainer}>
              <HotelLogoIcon hotelId={hotel.id} hotelType={hotel.hotelType} />
              <Typography variant="Title/Subtitle/lg">
                <h2>{hotel.name}</h2>
              </Typography>
              <div className={styles.addressContainer}>
                <address className={styles.address}>
                  {type == "mapListing" && (
                    <Typography variant="Body/Supporting text (caption)/smRegular">
                      <p>{addressStr}</p>
                    </Typography>
                  )}
                  {type === "pageListing" && (
                    <Link
                      size="small"
                      textDecoration="underline"
                      onClick={handleAddressClick}
                      href={mapUrl}
                      keepSearchParams
                      aria-label={intl.formatMessage({
                        id: "destination.seeOnMap",
                        defaultMessage: "See on map",
                      })}
                    >
                      <Typography variant="Body/Supporting text (caption)/smRegular">
                        <p>{addressStr}</p>
                      </Typography>
                    </Link>
                  )}
                </address>

                <div>
                  <Divider variant="vertical" />
                </div>
                <Typography variant="Body/Supporting text (caption)/smRegular">
                  <span>
                    {intl.formatMessage(
                      {
                        id: "common.kmToCityCenter",
                        defaultMessage: "{number} km to city center",
                      },
                      {
                        number: getSingleDecimal(distanceToCityCenter / 1000),
                      }
                    )}
                  </span>
                </Typography>
              </div>
            </div>

            {hotel.description ? (
              <Typography variant="Body/Paragraph/mdRegular">
                <p className={styles.hotelDescription}>{hotel.description}</p>
              </Typography>
            ) : null}

            <div className={styles.facilities}>
              {amenities.map((facility) => (
                <div className={styles.facilitiesItem} key={facility.id}>
                  <FacilityToIcon id={facility.id} color="CurrentColor" />
                  <Typography variant="Body/Supporting text (caption)/smRegular">
                    <span>{facility.name}</span>
                  </Typography>
                </div>
              ))}
            </div>
            {belowInfoSlot}
          </div>
          <PricesWrapper
            pathname={selectRate(lang)}
            isClickable={prices && !isDisabled}
            hotelId={hotel.id}
            removeBookingCodeFromSearchParams={!!(bookingCode && fullPrice)}
            searchParams={searchParams}
            onHoverStart={() => setIsPricesHovered(true)}
            onHoverEnd={() => setIsPricesHovered(false)}
          >
            {!prices ? (
              <NoPriceAvailableCard />
            ) : (
              <>
                {showBookingCodeChip && (
                  <BookingCodeChip
                    bookingCode={bookingCode}
                    isUnavailable={fullPrice}
                    isCampaignUnavailable={
                      isCampaignWithBookingCode && fullPrice
                    }
                    isCampaign={isCampaign && !(fullPrice && bookingCode)}
                  />
                )}
                {(!isUserLoggedIn ||
                  !prices?.member ||
                  (bookingCode && !fullPrice)) &&
                  prices?.public && (
                    <HotelPriceCard
                      productTypePrices={prices.public}
                      isPartnerBrand={isPartnerBrand}
                      className={styles.priceCard}
                      isCampaign={isCampaign}
                    />
                  )}
                {prices.member && (
                  <HotelPriceCard
                    productTypePrices={prices.member}
                    isPartnerBrand={isPartnerBrand}
                    className={styles.priceCard}
                    isMemberPrice
                  />
                )}
                {prices?.voucher && (
                  <HotelVoucherCard productTypeVoucher={prices.voucher} />
                )}
                {prices?.bonusCheque && (
                  <HotelChequeCard productTypeCheque={prices.bonusCheque} />
                )}
                {prices?.redemptions?.length ? (
                  <div className={styles.pointsCard}>
                    <Typography variant="Body/Supporting text (caption)/smRegular">
                      <p>
                        {intl.formatMessage({
                          id: "hotelCard.availableRates",
                          defaultMessage: "Available rates",
                        })}
                      </p>
                    </Typography>
                    {prices.redemptions.map((redemption) => (
                      <HotelPointsRow
                        key={redemption.rateCode}
                        pointsPerStay={redemption.localPrice.pointsPerStay}
                        additionalPricePerStay={
                          redemption.localPrice.additionalPricePerStay
                        }
                        additionalPriceCurrency={
                          redemption.localPrice.currency ?? undefined
                        }
                        pointsCurrency={pointsCurrency}
                      />
                    ))}
                  </div>
                ) : null}
                <FakeButton
                  variant="Primary"
                  size="md"
                  isDisabled={!!isDisabled}
                  isHovered={isPricesHovered}
                >
                  {isDisabled
                    ? notEnoughPointsLabel
                    : intl.formatMessage({
                        id: "common.seeRooms",
                        defaultMessage: "See rooms",
                      })}
                </FakeButton>
              </>
            )}
          </PricesWrapper>
        </div>
      </article>
    )
  }
)

export const HotelCard = HotelCardComponent as React.MemoExoticComponent<
  (props: HotelCardProps) => React.ReactElement
>

interface PricesWrapperProps {
  children: React.ReactNode
  isClickable?: boolean
  hotelId: string
  pathname: string
  removeBookingCodeFromSearchParams: boolean
  searchParams: ReadonlyURLSearchParams
  onHoverStart: () => void
  onHoverEnd: () => void
}
function PricesWrapper({
  children,
  hotelId,
  isClickable,
  pathname,
  removeBookingCodeFromSearchParams,
  searchParams,
  onHoverStart,
  onHoverEnd,
}: PricesWrapperProps) {
  const content = <div className={styles.prices}>{children}</div>

  if (!isClickable) {
    return content
  }

  const params = new URLSearchParams(searchParams)
  params.delete("city")
  params.set("hotel", hotelId)

  if (removeBookingCodeFromSearchParams) {
    params.delete("bookingCode")
  }

  const href = `${pathname}?${params.toString()}`

  return (
    <NextLink
      href={href}
      className={styles.link}
      onMouseEnter={onHoverStart}
      onMouseLeave={onHoverEnd}
    >
      {content}
    </NextLink>
  )
}
