"use client"

import { useRouter } from "next/navigation"
import {
  type PropsWithChildren,
  useCallback,
  useEffect,
  useLayoutEffect,
  useRef,
  useState,
} from "react"
import { useIntl } from "react-intl"

import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"

import { useDestinationDataStore } from "@/stores/destination-data"
import { useDestinationPageHotelsMapStore } from "@/stores/destination-page-hotels-map"

import DestinationFilterAndSort from "@/components/DestinationFilterAndSort"
import { BackToTopButton } from "@/components/TempDesignSystem/BackToTopButton"
import Button from "@/components/TempDesignSystem/Button"
import { useScrollToTop } from "@/hooks/useScrollToTop"
import { debounce } from "@/utils/debounce"

import DynamicMap from "./DynamicMap"
import MapContent from "./MapContent"
import MapProvider from "./MapProvider"
import { getHotelMapMarkers, mapMarkerDataToGeoJson } from "./utils"

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

import type { MapLocation } from "@/types/components/mapLocation"
import type { DestinationPagesHotelData } from "@/types/hotel"

interface MapProps {
  hotels: DestinationPagesHotelData[]
  mapId: string
  apiKey: string
  pageType: "city" | "country"
  defaultLocation: MapLocation
}

export default function Map({
  hotels,
  mapId,
  apiKey,
  defaultLocation,
  pageType,
  children,
}: PropsWithChildren<MapProps>) {
  const router = useRouter()
  const { activeMarker: activeHotelId, setActiveMarker } =
    useDestinationPageHotelsMapStore()
  const activeHotel = hotels.find(({ hotel }) => hotel.id === activeHotelId)
  const rootDiv = useRef<HTMLDivElement | null>(null)
  const [mapHeight, setMapHeight] = useState("100dvh")
  const scrollRef = useRef<HTMLElement>(null)
  const { showBackToTop, scrollToTop } = useScrollToTop({
    threshold: 550,
    elementRef: scrollRef,
    refScrollable: true,
  })

  const intl = useIntl()

  const { activeFilters } = useDestinationDataStore((state) => ({
    activeFilters: state.activeFilters,
  }))

  const hasActiveFilters = activeFilters.length > 0

  const markers = getHotelMapMarkers(hotels)
  const geoJson = mapMarkerDataToGeoJson(markers)
  const defaultCenter = activeHotel
    ? {
        lat: activeHotel.hotel.location.latitude,
        lng: activeHotel.hotel.location.longitude,
      }
    : defaultLocation
      ? {
          lat: defaultLocation.latitude,
          lng: defaultLocation.longitude,
        }
      : undefined
  const defaultZoom = activeHotel
    ? 15
    : (defaultLocation?.default_zoom ?? (pageType === "city" ? 10 : 3))

  // Calculate the height of the map based on the viewport height from the start-point (below the header and booking widget)
  const handleMapHeight = useCallback(() => {
    const topPosition = rootDiv.current?.getBoundingClientRect().top ?? 0
    setMapHeight(`calc(100dvh - ${topPosition}px)`)
  }, [])

  function handleClose() {
    const url = new URL(window.location.href)
    url.searchParams.delete("view")
    router.push(url.toString())
    setActiveMarker(null)
  }

  useLayoutEffect(() => {
    const originalOverflowY = document.body.style.overflowY

    document.body.style.overflowY = "hidden"

    // Making sure the map is always opened at the top of the page,
    // just below the header and booking widget as these should stay visible.
    // Reason for this is that we're using the hash property on pages which we preserve
    // when navigating between pages.
    window.scrollTo({ top: 0, behavior: "instant" })

    return () => {
      document.body.style.overflowY = originalOverflowY
    }
  }, [])

  useEffect(() => {
    const debouncedResizeHandler = debounce(function () {
      handleMapHeight()
    })

    const observer = new ResizeObserver(debouncedResizeHandler)
    observer.observe(document.documentElement)

    return () => {
      if (observer) {
        observer.unobserve(document.documentElement)
      }
    }
  }, [rootDiv, handleMapHeight])

  return (
    <MapProvider apiKey={apiKey} pageType={pageType}>
      <div
        className={styles.mapWrapper}
        style={{ height: mapHeight }}
        ref={rootDiv}
      >
        <div className={styles.mobileNavigation}>
          <Button
            intent="text"
            theme="base"
            variant="icon"
            onClick={handleClose}
          >
            <MaterialIcon icon="chevron_left" size={20} color="CurrentColor" />
            {intl.formatMessage({
              defaultMessage: "Back",
            })}
          </Button>
          <DestinationFilterAndSort
            listType={pageType === "city" ? "hotel" : "city"}
          />
        </div>
        <aside className={styles.sidebar} ref={scrollRef}>
          {children}
          {showBackToTop ? (
            <BackToTopButton position="left" onClick={scrollToTop} />
          ) : null}
        </aside>
        <DynamicMap
          markers={markers}
          mapId={mapId}
          onClose={handleClose}
          defaultCenter={defaultCenter}
          defaultZoom={defaultZoom}
          fitBounds={!activeHotel}
          gestureHandling="greedy"
        >
          <MapContent geojson={geoJson} disableClustering={hasActiveFilters} />
        </DynamicMap>
      </div>
    </MapProvider>
  )
}
