'use client'

import { useEffect, useMemo, useRef, useState } from 'react'
import { MapPin, Map as MapIcon, X, ChevronLeft, Check } from 'lucide-react'

export interface PickerPin {
  id: string
  x: number
  y: number
  locationId: string
  locationName: string
  locationPath: string
}

export interface PickerMap {
  id: string
  name: string
  imagePath: string
  width: number
  height: number
  /** Location this map belongs to. Used to drill into a pin's interior map. */
  ownerLocationId: string
  ownerPath: string | null
  pins: PickerPin[]
}

interface Crumb {
  locationId: string
  locationName: string
  locationPath: string
}

/**
 * Modal picker that drills through nested maps. It opens on the top-level
 * (property) maps. Tapping a pin whose location owns its own interior map
 * navigates into that map; tapping a pin with no interior map selects it via
 * `onPick(locationId, locationPath)` and dismisses. While drilled in, a "Use …"
 * button selects the current level itself, and a back chevron pops one level.
 */
export function LocationMapPicker({
  maps,
  trigger,
  onPick,
}: {
  maps: PickerMap[]
  trigger: React.ReactNode
  onPick: (locationId: string, locationPath: string) => void
}) {
  const [open, setOpen] = useState(false)
  // Drill path; empty = top-level view.
  const [stack, setStack] = useState<Crumb[]>([])
  // Pin whose label is currently revealed. Markers stay bare until tapped so a
  // dense map doesn't drown in overlapping labels.
  const [activePinId, setActivePinId] = useState<string | null>(null)

  const close = () => {
    setOpen(false)
    setStack([])
    setActivePinId(null)
  }

  // Lock the page behind the modal so touch drags can't scroll it or trigger
  // the browser's pull-to-refresh; restore on close/unmount.
  useEffect(() => {
    if (!open) return
    const prev = document.body.style.overflow
    document.body.style.overflow = 'hidden'
    return () => {
      document.body.style.overflow = prev
    }
  }, [open])

  // iOS Safari ignores `overscroll-behavior`, so pull-to-refresh still fires on
  // a downward drag. Intercept touchmove on the whole overlay with a non-passive
  // listener (a passive React onTouchMove can't call preventDefault) and cancel
  // the gesture everywhere except for in-range scrolling inside the list: drags
  // on the dimmed backdrop and header, and overscroll past the list's top/bottom
  // edges, are all swallowed so the page never moves or reloads.
  const overlayRef = useRef<HTMLDivElement>(null)
  const scrollRef = useRef<HTMLDivElement>(null)
  const startY = useRef(0)
  useEffect(() => {
    const overlay = overlayRef.current
    if (!open || !overlay) return
    const onStart = (e: TouchEvent) => {
      startY.current = e.touches[0].clientY
    }
    const onMove = (e: TouchEvent) => {
      const el = scrollRef.current
      const inList = el && e.target instanceof Node && el.contains(e.target)
      if (!inList) {
        e.preventDefault()
        return
      }
      const dy = e.touches[0].clientY - startY.current
      const atTop = el.scrollTop <= 0
      const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1
      const noScroll = el.scrollHeight <= el.clientHeight
      if (noScroll || (atTop && dy > 0) || (atBottom && dy < 0)) {
        e.preventDefault()
      }
    }
    overlay.addEventListener('touchstart', onStart, { passive: false })
    overlay.addEventListener('touchmove', onMove, { passive: false })
    return () => {
      overlay.removeEventListener('touchstart', onStart)
      overlay.removeEventListener('touchmove', onMove)
    }
  }, [open])

  const goBack = () => {
    setStack(s => s.slice(0, -1))
    setActivePinId(null)
  }

  // Locations that own at least one map can be drilled into.
  const mapOwners = useMemo(
    () => new Set(maps.map(m => m.ownerLocationId)),
    [maps],
  )
  // Locations pinned somewhere are reachable by drilling, so their maps are
  // not entry points. Anything left over is a top-level map.
  const pinnedLocations = useMemo(
    () => new Set(maps.flatMap(m => m.pins.map(p => p.locationId))),
    [maps],
  )

  const current = stack[stack.length - 1] ?? null
  const visibleMaps = useMemo(() => {
    if (current) return maps.filter(m => m.ownerLocationId === current.locationId)
    const roots = maps.filter(m => !pinnedLocations.has(m.ownerLocationId))
    // Fall back to all maps if nothing qualifies (e.g. every map is pinned).
    return roots.length ? roots : maps
  }, [maps, current, pinnedLocations])

  // Perform a pin's action: drill into its interior map, or select + close.
  const act = (p: PickerPin) => {
    if (mapOwners.has(p.locationId)) {
      setActivePinId(null)
      setStack(s => [
        ...s,
        { locationId: p.locationId, locationName: p.locationName, locationPath: p.locationPath },
      ])
    } else {
      onPick(p.locationId, p.locationPath)
      close()
    }
  }

  // First tap on a marker reveals its label; a second tap acts.
  const onMarkerClick = (e: React.MouseEvent, p: PickerPin) => {
    e.stopPropagation()
    if (activePinId === p.id) act(p)
    else setActivePinId(p.id)
  }

  return (
    <>
      <span onClick={() => setOpen(true)}>{trigger}</span>
      {open && (
        <div
          ref={overlayRef}
          data-no-pull-refresh
          className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/50 p-2 sm:p-4"
          onClick={close}
        >
          <div
            onClick={e => e.stopPropagation()}
            className="w-full max-w-2xl max-h-[90vh] rounded-2xl bg-background border shadow-xl flex flex-col"
          >
            <div className="flex items-center gap-2 p-3 border-b">
              {current && (
                <button
                  type="button"
                  onClick={goBack}
                  aria-label="Back"
                  className="text-muted-foreground hover:text-foreground p-1 shrink-0"
                >
                  <ChevronLeft className="size-5" />
                </button>
              )}
              <div className="min-w-0 flex-1">
                <div className="font-medium leading-tight">Pick a location on a map</div>
                {current && (
                  <div className="text-xs text-muted-foreground truncate">{current.locationPath}</div>
                )}
              </div>
              {current && (
                <button
                  type="button"
                  onClick={() => {
                    onPick(current.locationId, current.locationPath)
                    close()
                  }}
                  className="shrink-0 inline-flex items-center gap-1 rounded-lg bg-primary text-primary-foreground text-xs font-medium px-2.5 py-1.5 hover:opacity-90"
                >
                  <Check className="size-3.5" />
                  Use {current.locationName}
                </button>
              )}
              <button
                type="button"
                onClick={close}
                aria-label="Close"
                className="text-muted-foreground hover:text-foreground p-1 shrink-0"
              >
                <X className="size-5" />
              </button>
            </div>
            <div ref={scrollRef} className="overflow-y-auto overscroll-contain p-3 space-y-4">
              {maps.length === 0 ? (
                <div className="text-center text-sm text-muted-foreground py-12">
                  <MapIcon className="size-6 mx-auto mb-2 text-muted-foreground/60" />
                  No maps with pins yet. Upload one from Places → Maps.
                </div>
              ) : visibleMaps.length === 0 ? (
                <div className="text-center text-sm text-muted-foreground py-12">
                  <MapIcon className="size-6 mx-auto mb-2 text-muted-foreground/60" />
                  No interior map for {current?.locationName} yet.
                  {current && (
                    <div className="mt-3">
                      <button
                        type="button"
                        onClick={() => {
                          onPick(current.locationId, current.locationPath)
                          close()
                        }}
                        className="inline-flex items-center gap-1 rounded-lg bg-primary text-primary-foreground text-xs font-medium px-3 py-1.5 hover:opacity-90"
                      >
                        <Check className="size-3.5" />
                        Use {current.locationName}
                      </button>
                    </div>
                  )}
                </div>
              ) : (
                visibleMaps.map(m => (
                  <div key={m.id}>
                    <div className="flex items-center gap-2 mb-1">
                      <MapIcon className="size-4 text-muted-foreground shrink-0" />
                      <div className="text-sm font-medium truncate">{m.name}</div>
                      {m.ownerPath && (
                        <div className="text-xs text-muted-foreground truncate">
                          · {m.ownerPath}
                        </div>
                      )}
                    </div>
                    <div
                      className="relative w-full rounded-xl border bg-muted overflow-hidden"
                      style={{ aspectRatio: `${m.width} / ${m.height}` }}
                      onClick={() => setActivePinId(null)}
                    >
                      {/* eslint-disable-next-line @next/next/no-img-element */}
                      <img
                        src={`/api/uploads/${m.imagePath}`}
                        alt={m.name}
                        draggable={false}
                        className="absolute inset-0 w-full h-full object-contain pointer-events-none"
                      />
                      {m.pins.map(p => {
                        const isOwner = mapOwners.has(p.locationId)
                        const active = activePinId === p.id
                        return (
                          <div
                            key={p.id}
                            style={{ left: `${p.x * 100}%`, top: `${p.y * 100}%`, zIndex: active ? 20 : 10 }}
                            className="absolute -translate-x-1/2 -translate-y-full"
                          >
                            {active && (
                              <button
                                type="button"
                                onClick={e => { e.stopPropagation(); act(p) }}
                                className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 rounded-md bg-background border shadow-md text-xs font-medium whitespace-nowrap max-w-[70vw] flex items-center gap-1 hover:bg-accent/40"
                              >
                                <span className="truncate">{p.locationName}</span>
                                {isOwner ? (
                                  <ChevronLeft className="size-3 -rotate-180 text-muted-foreground shrink-0" />
                                ) : (
                                  <Check className="size-3 text-primary shrink-0" />
                                )}
                              </button>
                            )}
                            <button
                              type="button"
                              onClick={e => onMarkerClick(e, p)}
                              aria-label={p.locationName}
                              title={p.locationPath}
                              className="block hover:scale-110 transition-transform"
                            >
                              <MapPin
                                className={`size-6 text-primary-foreground drop-shadow ${
                                  active ? 'fill-primary scale-110' : 'fill-primary/90'
                                }`}
                              />
                            </button>
                          </div>
                        )
                      })}
                      {m.pins.length === 0 && (
                        <div className="absolute inset-0 flex items-center justify-center text-xs text-muted-foreground">
                          No pins on this map yet
                        </div>
                      )}
                    </div>
                  </div>
                ))
              )}
            </div>
          </div>
        </div>
      )}
    </>
  )
}
