import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import React, { useCallback, useEffect, useRef } from 'react';
import { GeoJSON, MapContainer, TileLayer, useMap, useMapEvents } from 'react-leaflet';
import { useMapUI, useMapViewport } from '@/contexts/MapContexts.tsx';
import bugulmaGeo from '@/data/bugulmaGeometry.json';
import MapControls from '@/components/map/MapControls.tsx';
import MatchLines from '@/components/map/MatchLines.tsx';
import ResourceFlowMarkers from '@/components/map/ResourceFlowMarkers.tsx';
import type { BackendMatch } from '@/schemas/backend/match';

// 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',
});

interface MatchesMapProps {
  matches: BackendMatch[];
  selectedMatchId: string | null;
  onMatchSelect: (matchId: string) => void;
}

/**
 * Component to handle map resize when sidebar opens/closes
 */
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);

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

  return null;
};

/**
 * Component to sync Leaflet map view with context state
 */
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
  useEffect(() => {
    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);

    const shouldUpdate = zoomDiff > 0.1 || centerDiff > 0.0001 || !lastUpdateRef.current;

    if (shouldUpdate) {
      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 };

      const timeoutId = setTimeout(() => {
        isUpdatingRef.current = false;
      }, 300);

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

  // Sync map state to context
  const mapEvents = useMapEvents({
    moveend: () => {
      if (!isUpdatingRef.current) {
        const center = mapEvents.getCenter();
        setMapCenter([center.lat, center.lng]);
      }
    },
    zoomend: () => {
      if (!isUpdatingRef.current) {
        setZoom(mapEvents.getZoom());
      }
    },
  });

  return null;
};

const MatchesMap: React.FC<MatchesMapProps> = ({ matches, selectedMatchId, onMatchSelect }) => {
  const { mapCenter, zoom } = useMapViewport();

  const whenCreated = useCallback((map: L.Map) => {
    // Fit bounds to Bugulma area on initial load
    if (bugulmaGeo) {
      const bounds = L.geoJSON(bugulmaGeo as GeoJSON.GeoJsonObject).getBounds();
      map.fitBounds(bounds, { padding: [20, 20] });
    }
  }, []);

  // GeoJSON styling for city boundary
  const geoJsonStyle = {
    color: 'hsl(var(--primary))',
    weight: 1.5,
    opacity: 0.8,
    fillOpacity: 0.1,
  };

  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 />

      {/* 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) => {
            layer.on({
              mouseover: () => {
                layer.setStyle({ weight: 2, opacity: 1 });
              },
              mouseout: () => {
                layer.setStyle({ weight: 1.5, opacity: 0.8 });
              },
            });
          }}
        />
      )}

      {/* Match connection lines */}
      <MatchLines
        matches={matches}
        selectedMatchId={selectedMatchId}
        onMatchSelect={onMatchSelect}
      />

      {/* Resource flow markers */}
      <ResourceFlowMarkers
        matches={matches}
        selectedMatchId={selectedMatchId}
        onMatchSelect={onMatchSelect}
      />

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

export default React.memo(MatchesMap);
