import React, {
  useEffect,
  useRef,
  useState,
  useCallback,
  useMemo,
} from "react";
import { Loader } from "@googlemaps/js-api-loader";
import { createRoot } from "react-dom/client";
import { Item } from "../types";
import ItemMarkerInfo from "./ItemMarkerInfo";

// Debounce utility function
function debounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: NodeJS.Timeout;
  return (...args: Parameters<T>) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => func(...args), wait);
  };
}

interface SearchResultsMapProps {
  items: Item[];
  searchLocation?: string;
  className?: string;
  style?: React.CSSProperties;
  onItemSelect?: (item: Item) => void;
}

const SearchResultsMap: React.FC<SearchResultsMapProps> = ({
  items,
  searchLocation,
  className,
  style,
  onItemSelect,
}) => {
  const mapRef = useRef<HTMLDivElement>(null);
  const mapInstanceRef = useRef<google.maps.Map | null>(null);
  const markersRef = useRef<google.maps.marker.AdvancedMarkerElement[]>([]);
  const infoWindowRef = useRef<google.maps.InfoWindow | null>(null);
  const mapInitializedRef = useRef<boolean>(false);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const apiKey = process.env.REACT_APP_GOOGLE_MAPS_PUBLIC_API_KEY;
  const mapId = process.env.REACT_APP_GOOGLE_MAPS_MAP_ID;

  // Clean up markers
  const clearMarkers = useCallback(() => {
    markersRef.current.forEach((marker) => {
      marker.map = null;
    });
    markersRef.current = [];
    if (infoWindowRef.current) {
      infoWindowRef.current.close();
    }
  }, []);

  // Group items by location
  const groupItemsByLocation = useCallback((items: Item[]) => {
    const groups: Map<string, Item[]> = new Map();

    items.forEach((item) => {
      if (item.latitude && item.longitude) {
        const key = `${item.latitude},${item.longitude}`;
        const existing = groups.get(key) || [];
        existing.push(item);
        groups.set(key, existing);
      }
    });

    return groups;
  }, []);

  // Create marker for a group of items at the same location
  const createLocationMarker = useCallback(
    async (items: Item[], map: google.maps.Map) => {
      if (items.length === 0 || !items[0].latitude || !items[0].longitude) return null;

      const { AdvancedMarkerElement, PinElement } =
        (await google.maps.importLibrary(
          "marker"
        )) as google.maps.MarkerLibrary;

      const itemCount = items.length;
      const isMobile = window.innerWidth < 768;

      // Create teardrop pin marker with badge for multiple items
      const pin = new PinElement({
        background: "#0d6efd",
        borderColor: "#ffffff",
        glyphColor: "#ffffff",
        scale: isMobile ? 1.2 : 1.0,
        glyph: itemCount > 1 ? `${itemCount}` : undefined,
      });

      // Add hover effects
      pin.element.style.cursor = "pointer";
      pin.element.style.transition = "transform 0.2s ease";
      pin.element.addEventListener("mouseenter", () => {
        pin.element.style.transform = "scale(1.1)";
      });
      pin.element.addEventListener("mouseleave", () => {
        pin.element.style.transform = "scale(1)";
      });

      const marker = new AdvancedMarkerElement({
        position: { lat: items[0].latitude, lng: items[0].longitude },
        map: map,
        content: pin.element,
        title: itemCount > 1 ? `${itemCount} items` : items[0].name,
      });

      // Add click listener
      marker.addListener("click", () => {
        if (infoWindowRef.current) {
          infoWindowRef.current.close();
        }

        const infoWindow = new google.maps.InfoWindow({
          headerDisabled: true,
          maxWidth:
            window.innerWidth < 768
              ? Math.min(320, window.innerWidth - 40)
              : 320,
        });

        // Create React container for info window content
        const infoContainer = document.createElement("div");
        const root = createRoot(infoContainer);

        if (itemCount === 1) {
          // Single item - show normal info
          root.render(
            <ItemMarkerInfo
              item={items[0]}
              onViewDetails={() => {
                infoWindow.close();
                if (onItemSelect) {
                  onItemSelect(items[0]);
                } else {
                  window.location.href = `/items/${items[0].id}`;
                }
              }}
            />
          );
        } else {
          // Multiple items - show list
          root.render(
            <div style={{ width: 'min(300px, 85vw)', maxHeight: '350px', overflowY: 'auto' }}>
              {items.map((item) => (
                <ItemMarkerInfo
                  key={item.id}
                  item={item}
                  onViewDetails={() => {
                    infoWindow.close();
                    if (onItemSelect) {
                      onItemSelect(item);
                    } else {
                      window.location.href = `/items/${item.id}`;
                    }
                  }}
                />
              ))}
            </div>
          );
        }

        infoWindow.setContent(infoContainer);
        infoWindow.open(map, marker);
        infoWindowRef.current = infoWindow;
      });

      return marker;
    },
    [onItemSelect]
  );

  // Add markers for all items (grouped by location)
  const addMarkersToMap = useCallback(
    async (map: google.maps.Map, items: Item[]) => {
      clearMarkers();

      const locationGroups = groupItemsByLocation(items);

      const groupsArray = Array.from(locationGroups.values());
      for (const groupItems of groupsArray) {
        const marker = await createLocationMarker(groupItems, map);
        if (marker) {
          markersRef.current.push(marker);
        }
      }
    },
    [createLocationMarker, clearMarkers, groupItemsByLocation]
  );

  // Calculate map bounds to fit all markers with appropriate zoom levels
  const fitMapToMarkers = useCallback((map: google.maps.Map, items: Item[]) => {
    const validItems = items.filter((item) => item.latitude && item.longitude);

    if (validItems.length === 0) return;

    const bounds = new google.maps.LatLngBounds();

    validItems.forEach((item) => {
      if (item.latitude && item.longitude) {
        bounds.extend(new google.maps.LatLng(item.latitude, item.longitude));
      }
    });

    if (validItems.length === 1) {
      // Single marker - center and set neighborhood-level zoom (not street-level)
      map.setCenter(bounds.getCenter());
      map.setZoom(10); // Changed from 12 to 10 for better context
    } else {
      // Multiple markers - calculate distance to determine zoom strategy
      const northeast = bounds.getNorthEast();
      const southwest = bounds.getSouthWest();

      // Calculate approximate distance between bounds corners in miles
      const distance =
        google.maps.geometry.spherical.computeDistanceBetween(
          southwest,
          northeast
        ) / 1609.34; // Convert meters to miles

      if (distance < 0.5) {
        // Very close items - show broader neighborhood context
        const center = bounds.getCenter();
        map.setCenter(center);
        map.setZoom(11); // Neighborhood level for very close items
      } else if (distance < 5) {
        // Moderate spread - use fitBounds with generous padding and min zoom
        map.fitBounds(bounds, 120); // Increased padding from 50 to 120

        // Ensure we don't zoom too close
        const currentZoom = map.getZoom();
        if (currentZoom && currentZoom > 12) {
          map.setZoom(12);
        }
      } else {
        // Wide spread - use fitBounds with standard padding
        map.fitBounds(bounds, 80);

        // Ensure minimum zoom for very spread out items
        const currentZoom = map.getZoom();
        if (currentZoom && currentZoom < 6) {
          map.setZoom(6);
        }
      }
    }
  }, []);

  // Check if any items have coordinates before initializing map
  const hasMappableItems = items.some(
    (item) => item.latitude && item.longitude
  );

  // Debounced function to update map markers (prevents excessive API calls during rapid updates)
  const debouncedUpdateMap = useMemo(
    () =>
      debounce(async (map: google.maps.Map, items: Item[]) => {
        try {
          setIsLoading(true);
          await addMarkersToMap(map, items);
          fitMapToMarkers(map, items);
        } catch (err) {
          console.error("Error updating map:", err);
        } finally {
          setIsLoading(false);
        }
      }, 300), // 300ms debounce delay
    [addMarkersToMap, fitMapToMarkers]
  );

  // Initialize map
  useEffect(() => {
    if (!apiKey || !mapRef.current || !hasMappableItems) return;

    // If map is already initialized, just update markers using debounced function
    if (mapInitializedRef.current && mapInstanceRef.current) {
      debouncedUpdateMap(mapInstanceRef.current, items);
      return;
    }

    let mounted = true;
    const initializeMap = async () => {
      try {
        setIsLoading(true);
        setError(null);

        const loader = new Loader({
          apiKey,
          version: "weekly",
        });

        await loader.importLibrary("maps");
        await loader.importLibrary("marker");
        await loader.importLibrary("geometry");

        if (!mounted || !mapRef.current) return;

        // Default map center (will be updated based on search results)
        const defaultCenter = { lat: 39.8283, lng: -98.5795 }; // Geographic center of US

        const mapConfig: google.maps.MapOptions = {
          mapId: mapId,
          center: defaultCenter,
          zoom: 4,
          maxZoom: 15, // Prevent users from zooming too close
          zoomControl: true,
          mapTypeControl: false,
          scaleControl: false,
          streetViewControl: false,
          rotateControl: false,
          fullscreenControl: true,
          mapTypeId: google.maps.MapTypeId.ROADMAP,
        };

        const map = new google.maps.Map(mapRef.current, mapConfig);

        mapInstanceRef.current = map;
        mapInitializedRef.current = true;

        // Add markers if items exist
        if (items.length > 0) {
          await addMarkersToMap(map, items);
          fitMapToMarkers(map, items);
        }
      } catch (err) {
        console.error("Failed to load Google Maps:", err);
        if (mounted) {
          setError("Failed to load map. Please try again later.");
        }
      } finally {
        if (mounted) {
          setIsLoading(false);
        }
      }
    };

    initializeMap();

    return () => {
      mounted = false;
      // Don't clear markers on cleanup - preserve for next use
    };
  }, [
    apiKey,
    mapId,
    hasMappableItems,
    items,
    addMarkersToMap,
    fitMapToMarkers,
    clearMarkers,
    debouncedUpdateMap,
  ]);

  // This useEffect is now handled by the main initialization effect above

  const defaultStyle = {
    height: "600px",
    width: "100%",
    borderRadius: "8px",
    backgroundColor: "#f8f9fa",
  };

  // Show message when no items have location data
  if (!hasMappableItems) {
    return (
      <div className={className} style={{ ...defaultStyle, ...style }}>
        <div className="d-flex align-items-center justify-content-center h-100">
          <div className="text-center">
            <i
              className="bi bi-geo-alt text-muted mb-3"
              style={{ fontSize: "3rem" }}
            ></i>
            <h5 className="text-muted">No Location Data</h5>
            <p className="text-muted small mb-0">
              None of the search results have location information available for
              mapping.
            </p>
          </div>
        </div>
      </div>
    );
  }

  if (error) {
    return (
      <div className={className} style={{ ...defaultStyle, ...style }}>
        <div className="d-flex align-items-center justify-content-center h-100">
          <div className="text-center">
            <i
              className="bi bi-exclamation-triangle text-warning mb-2"
              style={{ fontSize: "3rem" }}
            ></i>
            <p className="text-muted">{error}</p>
            <button
              className="btn btn-outline-primary btn-sm"
              onClick={() => window.location.reload()}
            >
              Retry
            </button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div
      className={className}
      style={{ ...defaultStyle, ...style, position: "relative" }}
    >
      {isLoading && (
        <div
          className="position-absolute d-flex align-items-center justify-content-center w-100 h-100"
          style={{
            backgroundColor: "rgba(248, 249, 250, 0.8)",
            borderRadius: "8px",
            zIndex: 1000,
          }}
        >
          <div className="text-center">
            <div className="spinner-border text-primary mb-2" role="status">
              <span className="visually-hidden">Loading map...</span>
            </div>
            <p className="text-muted small">Loading map...</p>
          </div>
        </div>
      )}

      <div
        ref={mapRef}
        style={{ width: "100%", height: "100%", borderRadius: "8px" }}
      />

      {!isLoading && items.length > 0 && (
        <div
          className="position-absolute bottom-0 start-0 bg-white px-3 py-2 rounded-top-2 shadow-sm"
          style={{ margin: "0 0 10px 10px", zIndex: 500 }}
        >
          <small className="text-muted">
            <i className="bi bi-geo-alt-fill text-primary me-1"></i>
            {
              items.filter((item) => item.latitude && item.longitude).length
            }{" "}
            items shown
          </small>
        </div>
      )}
    </div>
  );
};

export default SearchResultsMap;
