import {AppleMaps, type Coordinates} from '@crumbs/maps'
import {
  Link,
  useLocalSearchParams,
  usePreventZoomTransitionDismissal,
  useRouter,
} from 'expo-router'
import {useMemo, useRef, useState} from 'react'
import {View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'

import {Chip} from '@/components/chip'
import {MapFab} from '@/components/map-fab'
import {MapFilterChips} from '@/components/map-filter-chips'
import {Layout} from '@/components/ui/layout'
import {useCrumbComposer} from '@/hooks/use-crumb-composer'
import {useMapViewport} from '@/hooks/use-map-viewport'
import {
  buildClusterIndex,
  clusterExpansionZoom,
  clusterMarkers,
  parseClusterMarkerId,
} from '@/lib/cluster'
import {
  crumbCoords,
  isLocated,
  parseCoordParams,
  SF_DEFAULT_COORDS,
  toMarker,
} from '@/lib/geo'
import {
  encodeFilter,
  matchesFilter,
  parsePlaceMarkerId,
  placeMarker,
  type MapFilter,
} from '@/lib/map'
import {goBack} from '@/lib/navigation'
import {atoms as a, useTheme} from '@/sprinkles'
import {useAuth} from '@/stores/auth'
import {useCrumb, useRecentCrumbs} from '@/stores/crumbs'
import {useCycleIndex} from '@/stores/cycle-index'
import {
  useMapPlaces,
  useMapPlacesTruncated,
} from '@/stores/map-places'
import {useTags} from '@/stores/tags'
import {useTrail} from '@/stores/trails'

const DEFAULT_CAMERA = {
  coordinates: SF_DEFAULT_COORDS,
  zoom: 12,
}

// the overlay stack over the map, measured from the top inset
const OVERLAY_TOP = {back: 12, chips: 78, hint: 120}

const MapScreen = () => {
  const t = useTheme()
  const router = useRouter()
  const insets = useSafeAreaInsets()
  // the recent feed only hints where to open the camera (newest located
  // match); pins themselves come from the viewport endpoint
  const user = useAuth((s) => s.user)
  const crumbs = useRecentCrumbs()
  const tags = useTags()
  const places = useMapPlaces()
  const truncated = useMapPlacesTruncated()
  const [filter, setFilter] = useState<MapFilter>({kind: 'all'})
  const {open, openAt} = useCrumbComposer()
  const mapRef = useRef<AppleMaps.MapView>(null)

  usePreventZoomTransitionDismissal()

  const {lat, lng, zoom, crumbId, trailId} = useLocalSearchParams<{
    lat?: string
    lng?: string
    zoom?: string
    crumbId?: string
    trailId?: string
  }>()
  const trailMode = trailId != null && trailId !== ''

  // crumbs holds only the user's own — the focused crumb may belong
  // to a collaborator, so pull it from the detail cache
  const focusedCrumb = useCrumb(crumbId ?? '')
  const trail = useTrail(trailId ?? '')

  const cycleIndex = useCycleIndex()
  const {region, onCameraMove} = useMapViewport(trailMode, filter)

  // own pins come from the viewport endpoint; the feed only hints where to
  // open the camera (the newest matching crumb)
  const cameraHint = useMemo(
    () =>
      crumbs
        .filter(isLocated)
        .filter((c) => matchesFilter(c, filter)),
    [crumbs, filter],
  )
  const clusterIndex = useMemo(
    () => buildClusterIndex(places),
    [places],
  )

  // a focused collaborator crumb isn't in the author's places; pin it on
  // its own unless its place is already shown
  const extraFocused =
    !trailMode &&
    focusedCrumb &&
    isLocated(focusedCrumb) &&
    !places.some((p) => p.placeId === focusedCrumb.place.id)
      ? focusedCrumb
      : null

  const trailStops = useMemo(
    () => (trailMode ? (trail?.crumbs ?? []).filter(isLocated) : []),
    [trailMode, trail],
  )

  const markers = useMemo(() => {
    if (trailMode)
      // trail stops read in order, never grouped/clustered
      return trailStops.map((c, i) => ({
        ...toMarker(c, t.palette.bg, cycleIndex),
        title: `${i + 1}. ${c.name}`,
      }))
    // before the first camera event, fall back to per-place pins
    const ownMarkers = region
      ? clusterMarkers(clusterIndex, region, t.palette.bg)
      : places.map((p) => placeMarker(p, t.palette.bg))
    return [
      ...ownMarkers,
      ...(extraFocused
        ? [toMarker(extraFocused, t.palette.bg, cycleIndex)]
        : []),
    ]
  }, [
    trailMode,
    trailStops,
    region,
    clusterIndex,
    places,
    extraFocused,
    cycleIndex,
    t.palette.bg,
  ])

  const first = trailMode
    ? trailStops[0]
    : (cameraHint[0] ?? extraFocused)
  const focusCoords = parseCoordParams(lat, lng)
  const parsedZoom = Number(zoom)
  const focus = focusCoords
    ? {
        coordinates: focusCoords,
        zoom: Number.isFinite(parsedZoom) ? parsedZoom : 15,
      }
    : null
  const cameraPosition =
    focus ??
    (first
      ? {coordinates: crumbCoords(first), zoom: 14}
      : DEFAULT_CAMERA)

  const onMarkerClick = (marker: AppleMaps.Marker) => {
    if (!marker.id) return
    const clusterId = parseClusterMarkerId(marker.id)
    if (clusterId != null) {
      const zoomTo = clusterExpansionZoom(clusterIndex, clusterId)
      if (zoomTo != null && marker.coordinates)
        mapRef.current?.setCameraPosition({
          coordinates: marker.coordinates,
          zoom: zoomTo,
        })
      return
    }
    const placeId = parsePlaceMarkerId(marker.id)
    if (placeId) {
      // a single-crumb pin opens the crumb directly; only a place
      // with several crumbs is worth a list to pick from
      const point = places.find((p) => p.placeId === placeId)
      if (point && point.crumbCount === 1)
        router.push(`/crumb/${point.crumbId}`)
      else
        router.push(
          `/place/${placeId}?f=${encodeURIComponent(encodeFilter(filter))}`,
        )
      return
    }
    router.push(`/crumb/${marker.id}`)
  }

  return (
    <Layout.Screen>
      <Link.AppleZoomTarget>
        <AppleMaps.View
          ref={mapRef}
          style={a.flex_1}
          cameraPosition={cameraPosition}
          markers={markers}
          properties={{isMyLocationEnabled: true}}
          uiSettings={{
            myLocationButtonEnabled: false,
            togglePitchEnabled: false,
            compassEnabled: false,
          }}
          onMarkerClick={onMarkerClick}
          onCameraMove={onCameraMove}
          onMapClick={({coordinates}: {coordinates: Coordinates}) => {
            const {latitude, longitude} = coordinates
            if (latitude != null && longitude != null) {
              openAt({latitude, longitude})
            }
          }}
        />
      </Link.AppleZoomTarget>

      {!trailMode && (
        <MapFilterChips
          filter={filter}
          onChange={setFilter}
          tags={tags}
          style={[
            a.absolute,
            {top: insets.top + OVERLAY_TOP.chips, left: 0, right: 0},
          ]}
        />
      )}

      {!trailMode && truncated && (
        <View
          pointerEvents="none"
          style={[
            a.absolute,
            a.align_center,
            {top: insets.top + OVERLAY_TOP.hint, left: 0, right: 0},
          ]}
        >
          <Chip label="Zoom in to see all pins" />
        </View>
      )}

      <View
        style={[
          a.absolute,
          {top: insets.top + OVERLAY_TOP.back, left: 16},
        ]}
      >
        <MapFab label="Back" icon="chevron.left" onPress={goBack} />
      </View>

      <View
        style={[
          a.absolute,
          a.gap_sm,
          {bottom: insets.bottom + 16, right: 16},
        ]}
      >
        <MapFab
          label="Current location"
          icon="location"
          onPress={() => mapRef.current?.setCameraPosition()}
        />
        <MapFab
          label="Crumbs"
          icon="list.bullet"
          onPress={() =>
            user && router.push(`/profile/${user.id}/crumbs`)
          }
        />
        <MapFab
          label="New crumb"
          icon="plus"
          color="primary"
          onPress={open}
        />
      </View>
    </Layout.Screen>
  )
}

export default MapScreen
