import HistoricalMarkers from '@/components/map/HistoricalMarkers.tsx';
import MapControls from '@/components/map/MapControls.tsx';
import OrganizationCenterHandler from '@/components/map/OrganizationCenterHandler.tsx';
import OrganizationMarkers from '@/components/map/OrganizationMarkers.tsx';
import ProductServiceMarkers from '@/components/map/ProductServiceMarkers.tsx';
import SymbiosisLines from '@/components/map/SymbiosisLines.tsx';
import {
  useMapFilter,
  useMapInteraction,
  useMapUI,
  useMapViewport,
} from '@/contexts/MapContexts.tsx';
import bugulmaGeo from '@/data/bugulmaGeometry.json';
import { useMapData } from '@/hooks/map/useMapData.ts';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { useCallback, useEffect, useRef } from 'react';
import { GeoJSON, MapContainer, TileLayer, useMap, useMapEvents } from 'react-leaflet';
import MarkerClusterGroup from 'react-leaflet-markercluster';
import 'react-leaflet-markercluster/styles';

// Fix for default marker icon issue in Leaflet with webpack/vite
delete (L.Icon.Default.prototype as unknown as { _getIconUrl?: unknown })._getIconUrl;
L.Icon.Default.mergeOptions({
  iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon-2x.png',
  iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon.png',
  shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png',
});

/**
 * Component to handle map resize when sidebar opens/closes
 * Leaflet needs to recalculate its size when container dimensions change
 */
const MapResizeHandler = () => {
  const map = useMap();
  const { isSidebarOpen } = useMapUI();
  const resizeTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  useEffect(() => {
    // Clear any pending resize
    if (resizeTimeoutRef.current) {
      clearTimeout(resizeTimeoutRef.current);
    }

    // Delay resize to allow CSS transition to complete
    resizeTimeoutRef.current = setTimeout(() => {
      map.invalidateSize();
    }, 350); // Slightly longer than CSS transition (300ms)

    return () => {
      if (resizeTimeoutRef.current) {
        clearTimeout(resizeTimeoutRef.current);
      }
    };
  }, [map, isSidebarOpen]);

  return null;
};

/**
 * Component to sync Leaflet map view with context state
 * Optimized to prevent infinite loops and unnecessary updates
 */
const MapSync = () => {
  const { mapCenter, zoom, setZoom, setMapCenter } = useMapViewport();
  const map = useMap();
  const isUpdatingRef = useRef(false);
  const lastUpdateRef = useRef<{ center: [number, number]; zoom: number } | null>(null);

  // Sync context state to map (only when explicitly changed, not from map events)
  useEffect(() => {
    // Prevent updates during user interaction
    if (isUpdatingRef.current) return;

    const currentCenter = map.getCenter();
    const centerDiff =
      Math.abs(currentCenter.lat - mapCenter[0]) + Math.abs(currentCenter.lng - mapCenter[1]);
    const zoomDiff = Math.abs(map.getZoom() - zoom);

    // Only update if difference is significant (prevents micro-updates)
    const shouldUpdate = zoomDiff > 0.1 || centerDiff > 0.0001 || !lastUpdateRef.current;

    if (shouldUpdate) {
      // Check if this update would cause a loop
      const lastUpdate = lastUpdateRef.current;
      if (
        lastUpdate &&
        lastUpdate.center[0] === mapCenter[0] &&
        lastUpdate.center[1] === mapCenter[1] &&
        lastUpdate.zoom === zoom
      ) {
        return;
      }

      isUpdatingRef.current = true;
      map.setView(mapCenter, zoom, { animate: true });
      lastUpdateRef.current = { center: mapCenter, zoom };

      // Reset flag after animation completes
      const timeoutId = setTimeout(() => {
        isUpdatingRef.current = false;
      }, 300);

      return () => {
        clearTimeout(timeoutId);
      };
    }
  }, [map, mapCenter, zoom]);

  // Sync map events to context (throttled to avoid excessive updates)
  useMapEvents({
    moveend: () => {
      if (isUpdatingRef.current) return;
      const center = map.getCenter();
      const newCenter: [number, number] = [center.lat, center.lng];

      // Only update if center actually changed
      if (
        !lastUpdateRef.current ||
        Math.abs(lastUpdateRef.current.center[0] - newCenter[0]) > 0.0001 ||
        Math.abs(lastUpdateRef.current.center[1] - newCenter[1]) > 0.0001
      ) {
        setMapCenter(newCenter);
      }
    },
    zoomend: () => {
      if (isUpdatingRef.current) return;
      const newZoom = map.getZoom();

      // Only update if zoom actually changed
      if (!lastUpdateRef.current || Math.abs(lastUpdateRef.current.zoom - newZoom) > 0.1) {
        setZoom(newZoom);
      }
    },
  });

  return null;
};

/**
 * LeafletMap Component
 *
 * Renders an interactive map of Bugulma using Leaflet.
 * Features:
 * - Real city geometry from OSM Relation 9684457
 * - Organization and historical landmark markers with clustering
 * - AI-powered symbiosis connection visualization
 * - Zoom and pan controls
 */
const LeafletMap = () => {
  const { mapCenter, zoom } = useMapViewport();
  const { selectedOrg, hoveredOrgId, selectedLandmark, hoveredLandmarkId, symbiosisResult } =
    useMapInteraction();
  const { historicalLandmarks } = useMapData();
  const { filteredAndSortedOrgs } = useMapFilter();
  const { mapViewMode, showProductsServices } = useMapUI();

  // GeoJSON style function - show only edges, no fill to avoid darkening the area
  const geoJsonStyle = {
    fillColor: 'transparent',
    fillOpacity: 0,
    color: 'hsl(var(--border))',
    weight: 1.5,
    opacity: 0.8,
  };

  // Optimize map container with performance settings
  const whenCreated = useCallback((mapInstance: L.Map) => {
    // Enable preferCanvas for better performance with many markers
    mapInstance.options.preferCanvas = true;
    // Optimize rendering
    mapInstance.options.fadeAnimation = true;
    mapInstance.options.zoomAnimation = true;
    mapInstance.options.zoomAnimationThreshold = 4;

    // Remove Leaflet attribution control (removes flag and "Leaflet" branding)
    mapInstance.attributionControl.remove();
  }, []);

  return (
    <MapContainer
      center={mapCenter}
      zoom={zoom}
      minZoom={10}
      maxZoom={18}
      zoomControl={true}
      scrollWheelZoom={true}
      attributionControl={false}
      className="bg-background h-full w-full"
      preferCanvas={true}
      fadeAnimation={true}
      zoomAnimation={true}
      zoomAnimationThreshold={4}
      whenCreated={whenCreated}
    >
      <MapSync />
      <MapResizeHandler />
      <OrganizationCenterHandler />

      {/* Map tile layer */}
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />

      {/* City boundary GeoJSON */}
      {bugulmaGeo && (
        <GeoJSON
          data={bugulmaGeo as Parameters<typeof GeoJSON>[0]['data']}
          style={geoJsonStyle}
          onEachFeature={(feature, layer) => {
            // Optional: Add feature interaction handlers here if needed
            layer.on({
              mouseover: () => {
                layer.setStyle({ weight: 2, opacity: 1 });
              },
              mouseout: () => {
                layer.setStyle({ weight: 1.5, opacity: 0.8 });
              },
            });
          }}
        />
      )}

      {/* Symbiosis connection lines */}
      {mapViewMode === 'organizations' && symbiosisResult && selectedOrg && (
        <SymbiosisLines
          matches={symbiosisResult}
          hoveredOrgId={hoveredOrgId}
          selectedOrg={selectedOrg}
        />
      )}

      {/* Organization markers with clustering */}
      {mapViewMode === 'organizations' && (
        <MarkerClusterGroup
          chunkedLoading={true}
          chunkDelay={50}
          chunkInterval={100}
          maxClusterRadius={80}
          spiderfyOnMaxZoom={true}
          showCoverageOnHover={false}
          zoomToBoundsOnClick={true}
          disableClusteringAtZoom={15}
          removeOutsideVisibleBounds={false}
          animate={true}
          animateAddingMarkers={true}
          spiderfyDistanceMultiplier={2}
          spiderLegPolylineOptions={{
            weight: 1.5,
            color: 'hsl(var(--muted-foreground))',
            opacity: 0.5,
          }}
          iconCreateFunction={(cluster) => {
            const count = cluster.getChildCount();
            const size = count < 10 ? 'small' : count < 100 ? 'medium' : 'large';
            const className = `marker-cluster marker-cluster-${size}`;
            return L.divIcon({
              html: `<div><span>${count}</span></div>`,
              className,
              iconSize: L.point(40, 40),
            });
          }}
        >
          <OrganizationMarkers
            organizations={filteredAndSortedOrgs}
            selectedOrg={selectedOrg}
            hoveredOrgId={hoveredOrgId}
          />
        </MarkerClusterGroup>
      )}

      {/* Historical landmark markers */}
      {mapViewMode === 'historical' && (
        <HistoricalMarkers
          landmarks={historicalLandmarks}
          selectedLandmark={selectedLandmark}
          hoveredLandmarkId={hoveredLandmarkId}
        />
      )}

      {/* Products and Services markers */}
      {mapViewMode === 'organizations' && showProductsServices && (
        <MarkerClusterGroup
          chunkedLoading={true}
          chunkDelay={50}
          chunkInterval={100}
          maxClusterRadius={80}
          spiderfyOnMaxZoom={true}
          showCoverageOnHover={false}
          zoomToBoundsOnClick={true}
          disableClusteringAtZoom={15}
          removeOutsideVisibleBounds={false}
          animate={true}
          animateAddingMarkers={true}
          spiderfyDistanceMultiplier={2}
          spiderLegPolylineOptions={{
            weight: 1.5,
            color: 'hsl(var(--muted-foreground))',
            opacity: 0.5,
          }}
          iconCreateFunction={(cluster) => {
            const count = cluster.getChildCount();
            const size = count < 10 ? 'small' : count < 100 ? 'medium' : 'large';
            const className = `marker-cluster marker-cluster-${size}`;
            return L.divIcon({
              html: `<div><span>${count}</span></div>`,
              className,
              iconSize: L.point(40, 40),
            });
          }}
        >
          <ProductServiceMarkers showProducts={true} showServices={true} />
        </MarkerClusterGroup>
      )}

      {/* Map controls */}
      <MapControls />
    </MapContainer>
  );
};

export default LeafletMap;
