"use client"

import { useRouter, useSearchParams } from "next/navigation"
import { useEffect, useMemo, useRef } from "react"
import { useIntl } from "react-intl"

import { RateTypeEnum } from "@scandic-hotels/common/constants/rateType"
import {
  alternativeHotelsMap,
  selectHotelMap,
} from "@scandic-hotels/common/constants/routes/hotelReservation"
import { useScrollToTop } from "@scandic-hotels/common/hooks/useScrollToTop"
import { BackToTopButton } from "@scandic-hotels/design-system/BackToTopButton"
import { HotelCard } from "@scandic-hotels/design-system/HotelCard"

import {
  useBookingFlowConfig,
  useGetPointsCurrency,
} from "../../bookingFlowConfig/bookingFlowConfigContext"
import { useIsLoggedIn } from "../../hooks/useIsLoggedIn"
import useLang from "../../hooks/useLang"
import { mapApiImagesToGalleryImages } from "../../misc/imageGallery"
import {
  BookingCodeFilterEnum,
  useBookingCodeFilterStore,
} from "../../stores/bookingCode-filter"
import { useHotelFilterStore } from "../../stores/hotel-filters"
import { useHotelsMapStore } from "../../stores/hotels-map"
import { HotelDetailsSidePeek } from "../HotelDetailsSidePeek"
import { DEFAULT_SORT } from "../SelectHotel/HotelSorter"
import { getSortedHotels } from "./utils"

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

import type { HotelType } from "@scandic-hotels/common/constants/hotelType"

import type { HotelResponse } from "../SelectHotel/helpers"

export enum HotelCardListingTypeEnum {
  MapListing = "mapListing",
  PageListing = "pageListing",
}

type HotelCardListingProps = {
  hotelData: HotelResponse[]
  type?: HotelCardListingTypeEnum
  isAlternative?: boolean
}

export default function HotelCardListing({
  hotelData,
  type = HotelCardListingTypeEnum.PageListing,
  isAlternative,
}: HotelCardListingProps) {
  const router = useRouter()
  const lang = useLang()
  const intl = useIntl()
  const isUserLoggedIn = useIsLoggedIn()
  const searchParams = useSearchParams()
  const activeFilters = useHotelFilterStore((state) => state.activeFilters)
  const setResultCount = useHotelFilterStore((state) => state.setResultCount)
  const { activeHotel, activate, disengage, engage } = useHotelsMapStore()
  const { showBackToTop, scrollToTop } = useScrollToTop({ threshold: 490 })
  const activeCardRef = useRef<HTMLDivElement | null>(null)
  const pointsCurrency = useGetPointsCurrency()
  const config = useBookingFlowConfig()

  const sortBy = searchParams.get("sort") ?? DEFAULT_SORT

  const bookingCode = searchParams.get("bookingCode")
  // Special rates (corporate cheque, voucher) will not show regular rate hotels availability
  const isSpecialRate = bookingCode
    ? hotelData.find(
        (hotel) =>
          hotel.availability.productType?.bonusCheque ||
          hotel.availability.productType?.voucher
      )
    : false
  const activeCodeFilter = useBookingCodeFilterStore(
    (state) => state.activeCodeFilter
  )
  const isBookingCodeRateAvailable =
    bookingCode && !isSpecialRate
      ? hotelData.some((hotel) => hotel.availability.bookingCode)
      : false
  const showOnlyBookingCodeRates =
    isBookingCodeRateAvailable &&
    activeCodeFilter === BookingCodeFilterEnum.Discounted

  const hotelsWithBookingCode = hotelData.filter(
    (hotel) => hotel.availability?.bookingCode
  )
  const isCampaignWithBookingCode =
    !!bookingCode &&
    hotelsWithBookingCode.length > 0 &&
    hotelsWithBookingCode.every(
      (hotel) =>
        hotel.availability.productType?.public?.rateType ===
          RateTypeEnum.PublicPromotion ||
        hotel.availability.productType?.member?.rateType ===
          RateTypeEnum.PublicPromotion
    )

  const unfilteredHotelCount = showOnlyBookingCodeRates
    ? hotelData.filter((hotel) => hotel.availability.bookingCode).length
    : hotelData.length

  const hotels = useMemo(() => {
    const sortedHotels = getSortedHotels({
      hotels: hotelData,
      sortBy,
      bookingCode: isSpecialRate ? null : bookingCode,
    })

    const updatedHotelsList = showOnlyBookingCodeRates
      ? sortedHotels.filter((hotel) => hotel.availability.bookingCode)
      : sortedHotels

    if (!activeFilters.length) {
      return updatedHotelsList
    }

    return updatedHotelsList.filter((hotel) =>
      activeFilters.every((appliedFilterId) =>
        hotel.hotel.detailedFacilities.some(
          (facility) => facility.id.toString() === appliedFilterId
        )
      )
    )
  }, [
    activeFilters,
    bookingCode,
    hotelData,
    sortBy,
    showOnlyBookingCodeRates,
    isSpecialRate,
  ])

  useEffect(() => {
    if (activeCardRef.current && type === HotelCardListingTypeEnum.MapListing) {
      activeCardRef.current.scrollIntoView({
        behavior: "smooth",
        block: "nearest",
        inline: "center",
      })
    }
  }, [activeHotel, type])

  useEffect(() => {
    if (type === HotelCardListingTypeEnum.PageListing) {
      setResultCount(hotels.length, unfilteredHotelCount)
    }
  }, [hotels, setResultCount, type, unfilteredHotelCount])

  function isHotelActiveInMapView(hotelName: string): boolean {
    return (
      hotelName === activeHotel && type === HotelCardListingTypeEnum.MapListing
    )
  }

  return (
    <section className={styles.hotelCards}>
      {hotels.map((hotel) => (
        <div
          key={hotel.hotel.operaId}
          ref={isHotelActiveInMapView(hotel.hotel.name) ? activeCardRef : null}
          data-active={
            isHotelActiveInMapView(hotel.hotel.name) ? "true" : "false"
          }
        >
          <HotelCard
            hotel={{
              id: hotel.hotel.operaId,
              name: hotel.hotel.name,
              address: hotel.hotel.address,
              description: hotel.hotel.hotelContent.texts.descriptions?.short,
              hotelType: hotel.hotel.hotelType as HotelType,
              detailedFacilities: hotel.hotel.detailedFacilities,
              ratings: {
                tripAdvisor: hotel.hotel.ratings?.tripAdvisor.rating,
              },
            }}
            pointsCurrency={pointsCurrency}
            lang={lang}
            fullPrice={!hotel.availability.bookingCode}
            prices={
              hotel.availability.productType && {
                public: hotel.availability.productType?.public
                  ? {
                      ...hotel.availability.productType.public,
                      requestedPrice:
                        hotel.availability.productType?.public.requestedPrice ??
                        undefined,
                    }
                  : undefined,
                member: hotel.availability.productType?.member
                  ? {
                      ...hotel.availability.productType.member,
                      requestedPrice:
                        hotel.availability.productType?.member.requestedPrice ??
                        undefined,
                    }
                  : undefined,
                voucher: hotel.availability.productType?.voucher,
                bonusCheque: hotel.availability.productType?.bonusCheque
                  ? {
                      ...hotel.availability.productType.bonusCheque,
                      requestedPrice:
                        hotel.availability.productType.bonusCheque
                          .requestedPrice ?? undefined,
                    }
                  : undefined,
                redemptions: hotel.availability.productType?.redemptions?.map(
                  (redemption) => ({
                    ...redemption,
                    localPrice: {
                      ...redemption.localPrice,
                      currency: redemption.localPrice.currency,
                    },
                  })
                ),
              }
            }
            onFocusIn={() => engage(hotel.hotel.name)}
            onFocusOut={() => disengage()}
            onHover={() => engage(hotel.hotel.name)}
            onHoverEnd={() => disengage()}
            onAddressClick={() => {
              const mapUrl = isAlternative
                ? alternativeHotelsMap(lang)
                : selectHotelMap(lang)

              disengage() // Disengage the current hotel to avoid the hover state from being active when clicking on the address
              activate(hotel.hotel.name)
              router.push(`${mapUrl}?${searchParams.toString()}`)
            }}
            belowInfoSlot={
              <HotelDetailsSidePeek
                hotel={{ ...hotel.hotel, url: hotel.url }}
                restaurants={hotel.restaurants}
                additionalHotelData={hotel.additionalData}
                triggerLabel={intl.formatMessage({
                  id: "destination.seeHotelDetails",
                  defaultMessage: "See hotel details",
                })}
                buttonVariant="primary"
              />
            }
            distanceToCityCenter={hotel.hotel.location.distanceToCentre}
            images={mapApiImagesToGalleryImages(hotel.hotel.galleryImages)}
            isUserLoggedIn={isUserLoggedIn}
            state={
              isHotelActiveInMapView(hotel.hotel.name) ? "active" : "default"
            }
            type={type}
            bookingCode={bookingCode}
            isCampaignWithBookingCode={isCampaignWithBookingCode}
            isAlternative={isAlternative}
            isPartnerBrand={config.variant !== "scandic"}
          />
        </div>
      ))}
      {showBackToTop && (
        <BackToTopButton
          position="right"
          onClick={scrollToTop}
          label={intl.formatMessage({
            id: "common.backToTop",
            defaultMessage: "Back to top",
          })}
        />
      )}
    </section>
  )
}
