import { useRef, useEffect, useState } from 'react'
import { MapContainer, TileLayer, Marker, Popup, Polyline, Rectangle, useMap, useMapEvents } from 'react-leaflet'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import 'leaflet-polylinedecorator'
import { useTranslation } from 'react-i18next'
import DrawControl from './DrawControl'
import MapPoint from './MapPoint'
import MiniMap from './MiniMap'
import { defaultIcon } from './mapIcons'
import { getTileUrl } from './types'
import type { FlightPoint, CalculatedPointInfo, MapRectangle, ActionMode, MovingPointInfo } from './types'

interface MapEventsProps {
  points: FlightPoint[]
  handlePolylineClick: (e: L.LeafletMouseEvent) => void
  containerRef: React.RefObject<HTMLDivElement | null>
  onMapMove: (center: L.LatLng) => void
}

function MapEvents({ points, handlePolylineClick, containerRef, onMapMove }: MapEventsProps) {
  const map = useMap()
  const polylineRef = useRef<L.Polyline | null>(null)
  const arrowRef = useRef<L.FeatureGroup | null>(null)

  useEffect(() => {
    const handler = () => onMapMove(map.getCenter())
    map.on('moveend', handler)
    return () => { map.off('moveend', handler) }
  }, [map, onMapMove])

  useEffect(() => {
    if (polylineRef.current) map.removeLayer(polylineRef.current)
    if (arrowRef.current) map.removeLayer(arrowRef.current)

    if (points.length > 1) {
      const positions: L.LatLngTuple[] = points.map(p => [p.position.lat, p.position.lng])
      polylineRef.current = L.polyline(positions, { color: '#228be6', weight: 6, opacity: 0.7, lineJoin: 'round' }).addTo(map)
      arrowRef.current = L.polylineDecorator(polylineRef.current, {
        patterns: [{ offset: '10%', repeat: '40%', symbol: L.Symbol.arrowHead({ pixelSize: 12, pathOptions: { fillOpacity: 1, weight: 0, color: '#228be6' } }) }],
      }).addTo(map)
      polylineRef.current.on('click', handlePolylineClick)
    }

    const observer = new ResizeObserver(() => map.invalidateSize())
    if (containerRef.current) observer.observe(containerRef.current)

    return () => {
      if (polylineRef.current) { map.removeLayer(polylineRef.current); polylineRef.current = null }
      if (arrowRef.current) { map.removeLayer(arrowRef.current); arrowRef.current = null }
      observer.disconnect()
    }
  }, [map, points, handlePolylineClick, containerRef])

  return null
}

function SetView({ center }: { center: L.LatLngExpression }) {
  const map = useMap()
  useEffect(() => { map.setView(center) }, [center, map])
  return null
}

interface Props {
  points: FlightPoint[]
  calculatedPointInfo: CalculatedPointInfo[]
  currentPosition: { lat: number; lng: number }
  rectangles: MapRectangle[]
  setRectangles: React.Dispatch<React.SetStateAction<MapRectangle[]>>
  rectangleColor: string
  actionMode: ActionMode
  onAddPoint: (lat: number, lng: number) => void
  onUpdatePoint: (index: number, position: { lat: number; lng: number }) => void
  onRemovePoint: (id: string) => void
  onAltitudeChange: (index: number, altitude: number) => void
  onMetaChange: (index: number, meta: string[]) => void
  onPolylineClick: (e: L.LeafletMouseEvent) => void
  onPositionChange: (pos: { lat: number; lng: number }) => void
  onMapMove: (center: L.LatLng) => void
}

export default function FlightMap({
  points, currentPosition, rectangles, setRectangles,
  rectangleColor, actionMode, onAddPoint, onUpdatePoint, onRemovePoint,
  onAltitudeChange, onMetaChange, onPolylineClick, onPositionChange, onMapMove,
}: Props) {
  const { t } = useTranslation()
  const containerRef = useRef<HTMLDivElement>(null)
  const [movingPoint, setMovingPoint] = useState<MovingPointInfo | null>(null)
  const [draggablePoints, setDraggablePoints] = useState(points)
  const polylineClickRef = useRef(false)

  useEffect(() => { setDraggablePoints(points) }, [points])

  function ClickHandler() {
    useMapEvents({
      click(e) {
        if (actionMode === 'points') {
          if (!polylineClickRef.current) onAddPoint(e.latlng.lat, e.latlng.lng)
          polylineClickRef.current = false
        }
      },
    })
    return null
  }

  const handlePolylineClick = (e: L.LeafletMouseEvent) => {
    if (actionMode === 'points') {
      polylineClickRef.current = true
      onPolylineClick(e)
    }
  }

  const handleDrag = (index: number, pos: { lat: number; lng: number }) => {
    const updated = [...draggablePoints]
    updated[index] = { ...updated[index], position: pos }
    setDraggablePoints(updated)
  }

  return (
    <div className="flex-1 relative" ref={containerRef}>
      <MapContainer center={currentPosition} zoom={15} className="h-full w-full">
        <ClickHandler />
        <TileLayer
          url={getTileUrl()}
          crossOrigin="use-credentials"
          attribution="Satellite"
        />
        <MapEvents points={draggablePoints} handlePolylineClick={handlePolylineClick} containerRef={containerRef} onMapMove={onMapMove} />
        <SetView center={currentPosition} />

        {movingPoint && <MiniMap pointPosition={movingPoint} />}

        {draggablePoints.map((point, index) => (
          <MapPoint key={point.id}
            point={point} points={draggablePoints} index={index}
            mapElement={containerRef.current}
            onDrag={handleDrag}
            onDragEnd={(i, pos) => onUpdatePoint(i, pos)}
            onAltitudeChange={onAltitudeChange}
            onMetaChange={onMetaChange}
            onRemove={onRemovePoint}
            onMoving={setMovingPoint}
          />
        ))}

        {draggablePoints.length > 1 && (
          <Polyline
            positions={[[draggablePoints[draggablePoints.length - 1].position.lat, draggablePoints[draggablePoints.length - 1].position.lng],
              [draggablePoints[0].position.lat, draggablePoints[0].position.lng]]}
            color="#228be6" dashArray="5,10"
          />
        )}

        {currentPosition && (
          <Marker position={currentPosition} icon={defaultIcon} draggable
            eventHandlers={{ dragend: (e) => onPositionChange((e.target as L.Marker).getLatLng()) }}>
            <Popup>{t('flights.planner.currentLocation')}</Popup>
          </Marker>
        )}

        {rectangles.map(rect => (
          <Rectangle key={rect.id} bounds={rect.bounds} pathOptions={{ color: rect.color }} />
        ))}

        <DrawControl color={rectangleColor} actionMode={actionMode} rectangles={rectangles} setRectangles={setRectangles} />
      </MapContainer>

      {(actionMode === 'workArea' || actionMode === 'prohibitedArea') && (
        <div className="absolute top-2 left-1/2 -translate-x-1/2 z-[400] bg-az-panel/90 border border-az-border rounded px-3 py-1 text-[11px] text-az-text pointer-events-none">
          Click and drag on the map to draw a {actionMode === 'workArea' ? 'work area' : 'no-go zone'}
        </div>
      )}
    </div>
  )
}
