import { LatLngBounds } from 'leaflet';
import { useCallback, useEffect, useRef } from 'react';
import { useMap, useMapEvents } from 'react-leaflet';
import { useMapActions } from '@/contexts/MapContexts.tsx';

/**
 * Component to track map bounds and update context
 * This enables viewport-based lazy loading of organizations
 * Optimized with better debouncing and bounds comparison
 */
const MapBoundsTracker = () => {
  const { setMapBounds } = useMapActions();
  const boundsRef = useRef<LatLngBounds | null>(null);
  const updateTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const isUpdatingRef = useRef(false);

  /**
   * Check if bounds have changed significantly
   * Uses a more efficient comparison that considers both area and position
   */
  const hasBoundsChanged = useCallback(
    (newBounds: LatLngBounds, oldBounds: LatLngBounds | null): boolean => {
      if (!oldBounds) return true;

      // Compare center position
      const newCenter = newBounds.getCenter();
      const oldCenter = oldBounds.getCenter();
      const centerDiff =
        Math.abs(newCenter.lat - oldCenter.lat) + Math.abs(newCenter.lng - oldCenter.lng);

      // Compare bounds size (north-south and east-west spans)
      const newSize = {
        lat: newBounds.getNorth() - newBounds.getSouth(),
        lng: newBounds.getEast() - newBounds.getWest(),
      };
      const oldSize = {
        lat: oldBounds.getNorth() - oldBounds.getSouth(),
        lng: oldBounds.getEast() - oldBounds.getWest(),
      };
      const sizeDiff = Math.abs(newSize.lat - oldSize.lat) + Math.abs(newSize.lng - oldSize.lng);

      // Update if center moved significantly (>0.01 degrees) or size changed (>5%)
      return centerDiff > 0.01 || sizeDiff > Math.max(newSize.lat, newSize.lng) * 0.05;
    },
    []
  );

  const map = useMap();

  /**
   * Update bounds with debouncing
   */
  const updateBounds = useCallback(
    (immediate: boolean = false) => {
      if (updateTimeoutRef.current) {
        clearTimeout(updateTimeoutRef.current);
      }

      const update = () => {
        if (isUpdatingRef.current) return;

        const bounds = map.getBounds();
        if (hasBoundsChanged(bounds, boundsRef.current)) {
          boundsRef.current = bounds;
          isUpdatingRef.current = true;

          // Use requestAnimationFrame for smooth updates
          requestAnimationFrame(() => {
            setMapBounds(bounds);
            // Reset flag after a short delay
            setTimeout(() => {
              isUpdatingRef.current = false;
            }, 100);
          });
        }
      };

      if (immediate) {
        update();
      } else {
        updateTimeoutRef.current = setTimeout(update, 300);
      }
    },
    [map, setMapBounds, hasBoundsChanged]
  );

  useMapEvents({
    moveend: () => {
      updateBounds(false);
    },
    zoomend: () => {
      // Faster update on zoom as it's a discrete action
      updateBounds(false);
    },
    move: () => {
      // Throttle during active movement to reduce updates
      updateBounds(false);
    },
  });

  // Initialize bounds on mount
  useEffect(() => {
    // Wait for map to be fully initialized
    const timer = setTimeout(() => {
      updateBounds(true);
    }, 100);

    return () => {
      clearTimeout(timer);
      if (updateTimeoutRef.current) {
        clearTimeout(updateTimeoutRef.current);
      }
    };
  }, [updateBounds]);

  return null;
};

export default MapBoundsTracker;
