"use client"

import { Icon } from "leaflet"
import { MapPin, Navigation, Search } from "lucide-react"
import { useRef, useState } from "react"
import { MapContainer, Marker, TileLayer, useMapEvents } from "react-leaflet"
import { toast } from "sonner"

import "leaflet/dist/leaflet.css"

import { Button } from "@/src/components/ui/button"
import { Input } from "@/src/components/ui/input"

// Fix for default markers in react-leaflet with Webpack/Next.js
if (Icon.Default.prototype) {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  delete (Icon.Default.prototype as any)._getIconUrl
}

Icon.Default.mergeOptions({
  iconRetinaUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png",
  iconUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png",
  shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png",
})

interface LocationData {
  lat: number
  lng: number
  address?: string
}

interface MapPickerProps {
  onLocationSelect: (location: LocationData) => void
  defaultLocation?: LocationData
  className?: string
}

interface MapEventsProps {
  onLocationSelect: (location: LocationData) => void
}

function MapEvents({ onLocationSelect }: MapEventsProps) {
  const [isLoading, setIsLoading] = useState(false)

  useMapEvents({
    click: async (e) => {
      if (isLoading) return
      setIsLoading(true)
      try {
        const { lat, lng } = e.latlng
        const response = await fetch(
          `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`,
        )
        const data = response.ok ? await response.json() : null
        const address = data?.display_name || `${lat.toFixed(6)}, ${lng.toFixed(6)}`
        onLocationSelect({ lat, lng, address })
        toast.success(address, { description: "Emplacement sélectionné" })
      } catch {
        onLocationSelect({
          lat: e.latlng.lat,
          lng: e.latlng.lng,
          address: `${e.latlng.lat.toFixed(6)}, ${e.latlng.lng.toFixed(6)}`,
        })
      } finally {
        setIsLoading(false)
      }
    },
  })

  return null
}

export function MapPicker({ onLocationSelect, defaultLocation, className }: MapPickerProps) {
  const [selectedLocation, setSelectedLocation] = useState<LocationData | null>(defaultLocation || null)
  const [searchQuery, setSearchQuery] = useState("")
  const [isSearching, setIsSearching] = useState(false)
  const [isGettingLocation, setIsGettingLocation] = useState(false)
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const mapRef = useRef<any>(null)

  const defaultCenter: [number, number] = defaultLocation
    ? [defaultLocation.lat, defaultLocation.lng]
    : [12.3647, -1.5337] // Ouagadougou, Burkina Faso (default: +226)

  const handleLocationSelect = (location: LocationData) => {
    setSelectedLocation(location)
    onLocationSelect(location)
  }

  const getCurrentLocation = () => {
    if (!navigator.geolocation) {
      toast.error("La géolocalisation n'est pas supportée par votre navigateur.")
      return
    }
    setIsGettingLocation(true)
    navigator.geolocation.getCurrentPosition(
      async (position) => {
        try {
          const { latitude: lat, longitude: lng } = position.coords
          const response = await fetch(
            `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`,
          )
          const data = response.ok ? await response.json() : null
          const address = data?.display_name || `${lat.toFixed(6)}, ${lng.toFixed(6)}`
          const location = { lat, lng, address }
          handleLocationSelect(location)
          if (mapRef.current) mapRef.current.flyTo([lat, lng], 15)
          toast.success(address, { description: "Position actuelle trouvée" })
        } catch {
          const { latitude: lat, longitude: lng } = position.coords
          handleLocationSelect({ lat, lng, address: `${lat.toFixed(6)}, ${lng.toFixed(6)}` })
        } finally {
          setIsGettingLocation(false)
        }
      },
      () => {
        setIsGettingLocation(false)
        toast.error("Impossible d'accéder à votre position actuelle.")
      },
      { enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 },
    )
  }

  const handleUnifiedSearch = async () => {
    if (!searchQuery.trim()) return
    setIsSearching(true)

    let lat: number | null = null
    let lng: number | null = null

    try {
      const googleMapsMatch = searchQuery.match(/@(-?\d+\.?\d*),(-?\d+\.?\d*)/)
      if (googleMapsMatch) {
        lat = Number.parseFloat(googleMapsMatch[1])
        lng = Number.parseFloat(googleMapsMatch[2])
      } else {
        const coordsMatch = searchQuery.match(/(-?\d+\.?\d*),\s*(-?\d+\.?\d*)/)
        if (coordsMatch) {
          lat = Number.parseFloat(coordsMatch[1])
          lng = Number.parseFloat(coordsMatch[2])
        }
      }

      if (lat !== null && lng !== null && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
        const response = await fetch(
          `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`,
        )
        const data = response.ok ? await response.json() : null
        const address = data?.display_name || `${lat.toFixed(6)}, ${lng.toFixed(6)}`
        handleLocationSelect({ lat, lng, address })
        if (mapRef.current) mapRef.current.flyTo([lat, lng], 15)
        toast.success(address, { description: "Emplacement trouvé" })
        setSearchQuery("")
        return
      }

      const response = await fetch(
        `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchQuery)}&limit=1&addressdetails=1`,
      )
      if (!response.ok) throw new Error("Recherche échouée")

      const data = await response.json()
      if (data.length === 0) {
        toast.error("Aucun résultat trouvé. Essayez un autre terme.")
        return
      }

      const result = data[0]
      lat = Number.parseFloat(result.lat)
      lng = Number.parseFloat(result.lon)
      const address = result.display_name
      handleLocationSelect({ lat, lng, address })
      if (mapRef.current) mapRef.current.flyTo([lat, lng], 15)
      toast.success(address, { description: "Emplacement trouvé" })
    } catch {
      toast.error("Impossible de rechercher l'emplacement.")
    } finally {
      setIsSearching(false)
    }
  }

  return (
    <div className={`space-y-3 ${className ?? ""}`}>
      <div className="flex gap-2">
        <Input
          placeholder="Rechercher, coller un lien Google Maps ou saisir des coordonnées"
          value={searchQuery}
          onChange={(e) => setSearchQuery(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && handleUnifiedSearch()}
          className="flex-1"
        />
        <Button
          type="button"
          variant="outline"
          size="icon"
          onClick={handleUnifiedSearch}
          disabled={isSearching || !searchQuery.trim()}
        >
          <Search className="h-4 w-4" />
        </Button>
        <Button
          type="button"
          variant="outline"
          size="icon"
          onClick={getCurrentLocation}
          disabled={isGettingLocation}
        >
          <Navigation className="h-4 w-4" />
        </Button>
      </div>

      <div className="h-64 w-full rounded-md border overflow-hidden">
        <MapContainer
          center={defaultCenter}
          zoom={13}
          style={{ height: "100%", width: "100%" }}
          ref={mapRef}
        >
          <TileLayer
            attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          />
          <MapEvents onLocationSelect={handleLocationSelect} />
          {selectedLocation && (
            <Marker position={[selectedLocation.lat, selectedLocation.lng]} />
          )}
        </MapContainer>
      </div>

      {selectedLocation && (
        <div className="p-3 bg-muted rounded-md border">
          <div className="flex items-start gap-2">
            <MapPin className="h-4 w-4 text-muted-foreground mt-0.5 shrink-0" />
            <div className="min-w-0 flex-1">
              <p className="text-sm font-medium">Emplacement sélectionné</p>
              <p className="text-xs text-muted-foreground break-words">{selectedLocation.address}</p>
              <p className="text-xs text-muted-foreground mt-1">
                {selectedLocation.lat.toFixed(6)}, {selectedLocation.lng.toFixed(6)}
              </p>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}
