import type {CameraMoveEvent} from '@crumbs/maps'
import {useFocusEffect} from 'expo-router'
import {useCallback, useEffect, useRef, useState} from 'react'

import {type ClusterRegion} from '@/lib/cluster'
import {type MapFilter} from '@/lib/map'
import {useLoadMapPlaces} from '@/stores/map-places'

// fetch only after the camera settles, not on every move frame
const MAP_LOAD_DEBOUNCE_MS = 300

// the map's viewport data layer: tracks the camera region, debounces
// viewport fetches, refetches on filter change and on refocus (returning
// after an edit elsewhere). paused entirely in trail mode, where the pins
// come from the trail itself
export const useMapViewport = (
  paused: boolean,
  filter: MapFilter,
): {
  region: ClusterRegion | null
  onCameraMove: (e: CameraMoveEvent) => void
} => {
  const loadPlaces = useLoadMapPlaces()
  const [region, setRegion] = useState<ClusterRegion | null>(null)
  const regionRef = useRef<ClusterRegion | null>(null)
  const loadTimer = useRef<ReturnType<typeof setTimeout> | null>(null)

  useEffect(
    () => () => {
      if (loadTimer.current) clearTimeout(loadTimer.current)
    },
    [],
  )

  // a filter change invalidates the cached viewport; refetch at once
  useEffect(() => {
    if (paused) return
    const r = regionRef.current
    if (r) void loadPlaces(r, filter)
  }, [filter, paused, loadPlaces])

  // loadPlaces no-ops when the viewport is still covered
  useFocusEffect(
    useCallback(() => {
      if (paused) return
      const r = regionRef.current
      if (r) void loadPlaces(r, filter)
    }, [paused, loadPlaces, filter]),
  )

  const onCameraMove = useCallback(
    (e: CameraMoveEvent) => {
      const {latitude, longitude} = e.coordinates
      if (latitude == null || longitude == null) return
      const next = {
        center: {latitude, longitude},
        latitudeDelta: e.latitudeDelta,
        longitudeDelta: e.longitudeDelta,
        zoom: e.zoom,
      }
      regionRef.current = next
      setRegion(next)
      if (paused) return
      if (loadTimer.current) clearTimeout(loadTimer.current)
      loadTimer.current = setTimeout(
        () => void loadPlaces(next, filter),
        MAP_LOAD_DEBOUNCE_MS,
      )
    },
    [paused, filter, loadPlaces],
  )

  return {region, onCameraMove}
}
