import { useEffect, useMemo, useRef, useState } from 'react'
import MyLocationOutlinedIcon from '@mui/icons-material/MyLocationOutlined'
import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import CircularProgress from '@mui/material/CircularProgress'
import IconButton from '@mui/material/IconButton'
import List from '@mui/material/List'
import ListItemButton from '@mui/material/ListItemButton'
import ListItemText from '@mui/material/ListItemText'
import Stack from '@mui/material/Stack'
import TextField from '@mui/material/TextField'
import Tooltip from '@mui/material/Tooltip'
import Typography from '@mui/material/Typography'
import * as maplibregl from 'maplibre-gl'
import Map, { Marker, type MapMouseEvent, type MapRef } from 'react-map-gl/maplibre'
import { reverseGeocode, searchPlaces } from '../api/map-geocoding'
import type { LatLng, NominatimItem } from '../model/types'

export function AddressMapPicker(props: {
  value: { lat: number; lng: number } | null
  onChange: (v: { lat: number; lng: number; addressLine?: string | null }) => void
}) {
  const { value, onChange } = props
  const mapRef = useRef<MapRef | null>(null)
  const [q, setQ] = useState('')
  const [searching, setSearching] = useState(false)
  const [locating, setLocating] = useState(false)
  const [results, setResults] = useState<NominatimItem[]>([])
  const [hint, setHint] = useState<string | null>(null)
  const abortRef = useRef<AbortController | null>(null)
  const lastQueryRef = useRef<string>('')
  const lastRequestAtRef = useRef<number>(0)

  const qTrimmed = q.trim()
  const visibleResults = qTrimmed.length >= 3 ? results : []

  const center = useMemo(() => {
    if (value) return { lat: value.lat, lng: value.lng }
    return { lat: 55.751244, lng: 37.618423 } // Москва (fallback)
  }, [value])

  const pick = async (pos: LatLng) => {
    setHint(null)
    onChange({ lat: pos.lat, lng: pos.lng })
    try {
      const addr = await reverseGeocode(pos)
      if (addr) {
        setHint(addr)
        onChange({ lat: pos.lat, lng: pos.lng, addressLine: addr })
      }
    } catch {
      // ignore
    }
  }

  useEffect(() => {
    const s = qTrimmed
    if (s.length < 3) {
      return
    }

    const t = window.setTimeout(async () => {
      // throttle: не чаще 1 запроса в 900ms
      const now = Date.now()
      if (now - lastRequestAtRef.current < 900) return
      if (s === lastQueryRef.current) return

      lastQueryRef.current = s
      lastRequestAtRef.current = now

      abortRef.current?.abort()
      const ac = new AbortController()
      abortRef.current = ac

      setSearching(true)
      try {
        setResults(await searchPlaces(s, ac.signal))
      } catch (e) {
        if ((e as { name?: string })?.name !== 'AbortError') {
          setResults([])
        }
      } finally {
        setSearching(false)
      }
    }, 450)

    return () => {
      window.clearTimeout(t)
    }
  }, [qTrimmed])

  return (
    <Stack spacing={1.5}>
      <Typography variant="subtitle2">Выбор на карте</Typography>

      <Stack direction={{ xs: 'column', sm: 'row' }} spacing={1.5}>
        <TextField size="small" label="Найти адрес" value={q} onChange={(e) => setQ(e.target.value)} fullWidth />
        <Button
          variant="outlined"
          onClick={async () => {
            const s = q.trim()
            if (!s) return
            abortRef.current?.abort()
            const ac = new AbortController()
            abortRef.current = ac
            setSearching(true)
            try {
              lastQueryRef.current = s
              lastRequestAtRef.current = Date.now()
              setResults(await searchPlaces(s, ac.signal))
            } finally {
              setSearching(false)
            }
          }}
          disabled={searching || !q.trim()}
          sx={{ minWidth: 160 }}
        >
          {searching ? <CircularProgress size={18} /> : 'Найти'}
        </Button>
      </Stack>

      {visibleResults.length > 0 && (
        <List dense sx={{ border: 1, borderColor: 'divider', borderRadius: 2 }}>
          {visibleResults.map((r) => (
            <ListItemButton
              key={`${r.lat}:${r.lon}:${r.display_name}`}
              onClick={() => {
                const lat = Number(r.lat)
                const lng = Number(r.lon)
                if (!Number.isFinite(lat) || !Number.isFinite(lng)) return
                void pick({ lat, lng })
              }}
            >
              <ListItemText primary={r.display_name} />
            </ListItemButton>
          ))}
        </List>
      )}

      <Box
        sx={{
          height: 280,
          borderRadius: 2,
          overflow: 'hidden',
          border: 1,
          borderColor: 'divider',
          position: 'relative',
        }}
      >
        <Map
          mapLib={maplibregl}
          ref={mapRef}
          initialViewState={{ latitude: center.lat, longitude: center.lng, zoom: 12 }}
          style={{ width: '100%', height: 280 }}
          mapStyle={{
            version: 8,
            sources: {
              osm: {
                type: 'raster',
                tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
                tileSize: 256,
                attribution: '© OpenStreetMap contributors',
              },
            },
            layers: [{ id: 'osm', type: 'raster', source: 'osm' }],
          }}
          onClick={(e: MapMouseEvent) => {
            const { lng, lat } = e.lngLat
            void pick({ lat, lng })
          }}
        >
          {value && (
            <Marker
              longitude={value.lng}
              latitude={value.lat}
              draggable
              onDragEnd={(e) => {
                const { lng, lat } = e.lngLat
                void pick({ lat, lng })
              }}
              anchor="bottom"
            >
              <Box
                sx={{
                  width: 18,
                  height: 18,
                  bgcolor: 'primary.main',
                  borderRadius: '50%',
                  border: 2,
                  borderColor: 'background.paper',
                  boxShadow: 3,
                }}
              />
            </Marker>
          )}
        </Map>

        <Tooltip title="Моё местоположение">
          <span>
            <IconButton
              size="small"
              disabled={locating || !('geolocation' in navigator)}
              onClick={() => {
                if (!('geolocation' in navigator)) return
                setLocating(true)
                navigator.geolocation.getCurrentPosition(
                  (pos) => {
                    const lat = pos.coords.latitude
                    const lng = pos.coords.longitude
                    mapRef.current?.flyTo({ center: [lng, lat], zoom: 15, duration: 800 })
                    void pick({ lat, lng })
                    setLocating(false)
                  },
                  () => {
                    setLocating(false)
                  },
                  { enableHighAccuracy: true, timeout: 8000, maximumAge: 60_000 },
                )
              }}
              sx={{
                position: 'absolute',
                top: 10,
                right: 10,
                bgcolor: 'background.paper',
                border: 1,
                borderColor: 'divider',
                boxShadow: 2,
                '&:hover': { bgcolor: 'background.paper' },
              }}
              aria-label="Моё местоположение"
            >
              {locating ? <CircularProgress size={16} /> : <MyLocationOutlinedIcon fontSize="small" />}
            </IconButton>
          </span>
        </Tooltip>
      </Box>

      <Box sx={{ minHeight: 32, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
        {hint && (
          <Typography variant="caption" color="text.secondary">
            Подсказка адреса: {hint}
          </Typography>
        )}
      </Box>
    </Stack>
  )
}
