"use client"

import { useCallback, useEffect, useRef } from "react"
import { useIntl } from "react-intl"

import { useHotelsMapStore } from "../../stores/hotels-map"
import ListingHotelCardDialog from "../ListingHotelCardDialog"
import { getHotelPins } from "./utils"

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

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

interface HotelCardDialogListingProps {
  hotels: HotelResponse[]
}

export default function HotelCardDialogListing({
  hotels,
}: HotelCardDialogListingProps) {
  const intl = useIntl()
  const isRedemption = hotels?.find(
    (hotel) => hotel.availability.productType?.redemptions?.length
  )
  const currencyValue = isRedemption
    ? intl.formatMessage({
        id: "common.points",
        defaultMessage: "Points",
      })
    : undefined
  const hotelsPinData = getHotelPins(hotels, intl, currencyValue)
  const activeCardRef = useRef<HTMLDivElement | null>(null)
  const observerRef = useRef<IntersectionObserver | null>(null)
  const dialogRef = useRef<HTMLDivElement>(null)
  const isScrollingRef = useRef<boolean>(false)
  const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const { activeHotel, activate, deactivate } = useHotelsMapStore()

  const handleIntersection = useCallback(
    (entries: IntersectionObserverEntry[]) => {
      // skip intersection handling during scrolling
      if (isScrollingRef.current) return

      if (debounceTimerRef.current) {
        clearTimeout(debounceTimerRef.current)
      }

      debounceTimerRef.current = setTimeout(() => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            const cardName = entry.target.getAttribute("data-name")
            if (cardName && cardName !== activeHotel) {
              activate(cardName)
            }
          }
        })
      }, 100)
    },
    [activate, activeHotel]
  )

  useEffect(() => {
    observerRef.current = new IntersectionObserver(handleIntersection, {
      root: null,
      threshold: [0.3, 0.5, 0.7],
    })

    const elements = document.querySelectorAll("[data-name]")
    elements.forEach((el) => observerRef.current?.observe(el))

    return () => {
      if (debounceTimerRef.current) {
        clearTimeout(debounceTimerRef.current)
      }

      elements.forEach((el) => observerRef.current?.unobserve(el))
      observerRef.current?.disconnect()
      observerRef.current = null
    }
  }, [handleIntersection])

  useEffect(() => {
    if (activeCardRef.current) {
      isScrollingRef.current = true

      requestAnimationFrame(() => {
        activeCardRef.current?.scrollIntoView({
          behavior: "smooth",
          block: "nearest",
          inline: "center",
        })

        setTimeout(() => {
          isScrollingRef.current = false
        }, 800)
      })
    }
  }, [activeHotel])

  useEffect(() => {
    const handleMapClick = (e: MouseEvent) => {
      // ignore clicks within the dialog
      if (dialogRef.current?.contains(e.target as Node)) {
        return
      }

      // ignore clicks on hotel pins
      const target = e.target as HTMLElement
      if (target.closest("[data-hotelpin]")) {
        return
      }

      deactivate()
    }

    if (activeHotel) {
      document.addEventListener("click", handleMapClick)
    }

    return () => {
      document.removeEventListener("click", handleMapClick)
    }
  }, [dialogRef, activeHotel, deactivate])

  return (
    <div className={styles.hotelCardDialogListing} ref={dialogRef}>
      {hotelsPinData?.map((data) => {
        const isActive = data.name === activeHotel
        return (
          <div
            key={data.name}
            ref={isActive ? activeCardRef : null}
            data-name={data.name}
            className={styles.hotelCard}
          >
            <ListingHotelCardDialog data={data} handleClose={deactivate} />
          </div>
        )
      })}
    </div>
  )
}
