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

let maplibreglPromise: Promise<typeof maplibregl> | null = null

function loadMaplibre() {
  if (!maplibreglPromise) {
    maplibreglPromise = Promise.all([import('maplibre-gl'), import('maplibre-gl/dist/maplibre-gl.css')]).then(
      ([mod]) => mod,
    )
  }
  return maplibreglPromise
}

type MapPickerMapProps = {
  value: { lat: number; lng: number } | null
  onChange: (v: { lat: number; lng: number; addressLine?: string | null }) => void
  center: { lat: number; lng: number }
}

export function MapPickerMap({ value, onChange, center }: MapPickerMapProps) {
  const mapRef = useRef<MapRef | null>(null)
  const [maplibre, setMaplibre] = useState<typeof maplibregl | null>(null)
  const [loading, setLoading] = useState(true)
  const [locating, setLocating] = useState(false)

  useEffect(() => {
    let cancelled = false
    loadMaplibre().then((mod) => {
      if (!cancelled) {
        setMaplibre(mod)
        setLoading(false)
      }
    })
    return () => {
      cancelled = true
    }
  }, [])

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

  if (loading || !maplibre) {
    return (
      <Box
        sx={{
          height: 280,
          borderRadius: 2,
          border: 1,
          borderColor: 'divider',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        <CircularProgress size={24} />
      </Box>
    )
  }

  return (
    <Box
      sx={{
        height: 280,
        borderRadius: 2,
        overflow: 'hidden',
        border: 1,
        borderColor: 'divider',
        position: 'relative',
      }}
    >
      <Map
        mapLib={maplibre}
        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>
  )
}
