'use client';

import { useEffect, useRef, useState, useCallback } from 'react';

interface GoogleMapProps {
  address: string;
  locationName?: string;
  className?: string;
}

// Google Maps API types
interface GoogleMaps {
  maps: {
    Geocoder: new () => GoogleGeocoder;
    Map: new (element: HTMLElement, options: GoogleMapOptions) => GoogleMapInstance;
    GeocoderStatus: {
      OK: string;
      [key: string]: string;
    };
    importLibrary: (library: string) => Promise<{ AdvancedMarkerElement: new (options: GoogleMarkerOptions) => GoogleMarker }>;
  };
}

interface GoogleGeocoder {
  geocode: (request: { address: string }, callback: (results: GoogleGeocodeResult[] | null, status: string) => void) => void;
}

interface GoogleGeocodeResult {
  geometry: {
    location: GoogleLatLng;
  };
}

interface GoogleLatLng {
  lat: () => number;
  lng: () => number;
}

interface GoogleMapOptions {
  zoom: number;
  center: GoogleLatLng;
  mapId: string;
  mapTypeControl: boolean;
  streetViewControl: boolean;
  fullscreenControl: boolean;
  zoomControl: boolean;
}

interface GoogleMapInstance {
  markers?: GoogleMarker[];
}

interface GoogleMarkerOptions {
  map: GoogleMapInstance;
  position: GoogleLatLng;
  title: string;
}

interface GoogleMarker {
  map: GoogleMapInstance | null;
}

declare global {
  interface Window {
    google?: GoogleMaps;
    [key: string]: unknown; // Allow dynamic callback properties
  }
}

const GOOGLE_MAPS_API_KEY = 'AIzaSyAb4-zSsPFx-QGi4cAiCGaRrzsAJC6e348';

export default function GoogleMap({ address, locationName, className = '' }: GoogleMapProps) {
  const mapRef = useRef<HTMLDivElement>(null);
  const [mapError, setMapError] = useState<string | null>(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const geocoderRef = useRef<GoogleGeocoder | null>(null);
  const mapInstanceRef = useRef<GoogleMapInstance | null>(null);
  const isInitializingRef = useRef(false);
  const callbackNameRef = useRef<string | null>(null);

  // Initialize map once Google Maps API is loaded
  // Use ref to store callback to avoid "accessed before declaration" error
  const initMapRef = useRef<(() => Promise<void>) | null>(null);
  
  const initMap = useCallback(async () => {
    // Prevent multiple simultaneous initializations
    if (isInitializingRef.current) return;
    
    // Ensure DOM element exists and Google Maps is fully loaded
    if (!mapRef.current || !window.google || !window.google.maps) {
      return;
    }

    // Check if Geocoder is available (required for async loading)
    if (!window.google.maps.Geocoder) {
      console.warn('Google Maps Geocoder not yet available, retrying...');
      setTimeout(() => {
        if (mapRef.current && window.google?.maps?.Geocoder && initMapRef.current) {
          void initMapRef.current();
        }
      }, 100);
      return;
    }

    // Double-check the element is still in the DOM
    if (!document.body.contains(mapRef.current)) {
      return;
    }

    isInitializingRef.current = true;

    try {
      // Clear any existing map instance
      if (mapInstanceRef.current) {
        // Clear markers and listeners
        const markers = mapInstanceRef.current.markers || [];
        markers.forEach((marker) => {
          if (marker.map) marker.map = null;
        });
        mapInstanceRef.current = null;
      }

      // Import marker library for AdvancedMarkerElement
      const markerLibrary = await window.google.maps.importLibrary("marker");
      const AdvancedMarkerElement = markerLibrary.AdvancedMarkerElement;

      // Create geocoder - now we know it's available
      geocoderRef.current = new window.google.maps.Geocoder();
      
      // Geocode the address
      geocoderRef.current.geocode({ address }, (results: GoogleGeocodeResult[] | null, status: string) => {
        // Check if component is still mounted and ref is still valid
        if (!mapRef.current || !document.body.contains(mapRef.current)) {
          isInitializingRef.current = false;
          return;
        }

        if (status === 'OK' && results && results[0]) {
          const location = results[0].geometry.location;
          
          // Create map centered on the geocoded location
          // Advanced markers require a mapId
          // Note: When using mapId, styles are controlled via Google Cloud Console, not in code
          if (!window.google) return;
          const map = new window.google.maps.Map(mapRef.current, {
            zoom: 15,
            center: location,
            mapId: 'DEMO_MAP_ID', // Can be replaced with a custom Map ID from Google Cloud Console
            mapTypeControl: false,
            streetViewControl: false,
            fullscreenControl: true,
            zoomControl: true,
            // Styles cannot be set when mapId is present - configure in Google Cloud Console instead
          });

          // Create AdvancedMarkerElement (modern, non-deprecated approach)
          const marker = new AdvancedMarkerElement({
            map: map,
            position: location,
            title: locationName || address,
          });

          // Store markers for cleanup
          map.markers = [marker];
          mapInstanceRef.current = map;
          setIsLoaded(true);
          setMapError(null);
        } else {
          // If geocoding fails, show error
          setMapError('Unable to find the address. Please check the address and try again.');
          setIsLoaded(false);
        }
        
        isInitializingRef.current = false;
      });
    } catch (error) {
      console.error('Error initializing map:', error);
      setMapError('Error loading map. Please try again later.');
      isInitializingRef.current = false;
    }
  }, [address, locationName]);

  // Store callback in ref (use effect to avoid accessing refs during render)
  useEffect(() => {
    initMapRef.current = initMap;
  }, [initMap]);

  // Load Google Maps script with proper callback
  useEffect(() => {
    let mounted = true;
    let checkInterval: NodeJS.Timeout | null = null;

    // Generate unique callback name for this component instance
    const callbackName = `initGoogleMap_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    callbackNameRef.current = callbackName;

    // Create callback function that will be called when API loads
    (window as Window & { [key: string]: () => void })[callbackName] = () => {
      if (mounted && mapRef.current && initMapRef.current) {
        // Small delay to ensure everything is ready
        setTimeout(() => {
          if (mounted && mapRef.current && initMapRef.current) {
            void initMapRef.current();
          }
        }, 100);
      }
    };

    // Check if script is already loaded
    if (window.google && window.google.maps && window.google.maps.Geocoder) {
      setTimeout(() => {
        if (mounted && mapRef.current && initMapRef.current) {
          void initMapRef.current();
        }
      }, 100);
      return () => {
        mounted = false;
        delete (window as Window & { [key: string]: unknown })[callbackName];
      };
    }

    // Check if script tag already exists (shared script)
    const existingScript = document.querySelector(`script[src*="maps.googleapis.com"]`) as HTMLScriptElement;
    if (existingScript) {
      // Script is loading or loaded, wait for it
      checkInterval = setInterval(() => {
        if (window.google && window.google.maps && window.google.maps.Geocoder && mounted) {
          clearInterval(checkInterval!);
          setTimeout(() => {
            if (mounted && mapRef.current && initMapRef.current) {
              void initMapRef.current();
            }
          }, 100);
        }
      }, 100);
      
      return () => {
        mounted = false;
        if (checkInterval) {
          clearInterval(checkInterval);
        }
        delete (window as Window & { [key: string]: unknown })[callbackName];
      };
    }

    // Create and load script with callback
    // Include 'marker' library for AdvancedMarkerElement
    const script = document.createElement('script');
    script.src = `https://maps.googleapis.com/maps/api/js?key=${GOOGLE_MAPS_API_KEY}&libraries=places,marker&loading=async&callback=${callbackName}`;
    script.async = true;
    script.defer = true;
    
    script.onerror = () => {
      if (mounted) {
        setMapError('Failed to load Google Maps. Please check your internet connection.');
        delete (window as Window & { [key: string]: unknown })[callbackName];
      }
    };
    
    document.head.appendChild(script);
    
    return () => {
      mounted = false;
      if (checkInterval) {
        clearInterval(checkInterval);
      }
      // Clean up callback
      if (callbackNameRef.current) {
        delete (window as Window & { [key: string]: unknown })[callbackNameRef.current];
        callbackNameRef.current = null;
      }
      // Never remove the script - it's shared and removing it causes React errors
    };
  }, []); // Only run once on mount

  // Re-initialize map if address changes (but only after API is loaded)
  useEffect(() => {
    if (window.google && window.google.maps && window.google.maps.Geocoder && mapRef.current && !isInitializingRef.current && initMapRef.current) {
      // Small delay to ensure DOM is stable
      const timeoutId = setTimeout(() => {
        if (mapRef.current && initMapRef.current) {
          void initMapRef.current();
        }
      }, 100);
      
      return () => clearTimeout(timeoutId);
    }
  }, [address, locationName]);

  // Cleanup map instance on unmount
  useEffect(() => {
    return () => {
      if (mapInstanceRef.current) {
      // Clear markers
      const markers = mapInstanceRef.current.markers || [];
      markers.forEach((marker) => {
        if (marker.map) marker.map = null;
      });
        mapInstanceRef.current = null;
      }
      isInitializingRef.current = false;
    };
  }, []);

  return (
    <div className={`h-full w-full rounded-lg ${className}`} style={{ minHeight: '240px', position: 'relative' }}>
      {/* Map container - always present, Google Maps will render into it */}
      <div 
        ref={mapRef} 
        className="h-full w-full rounded-lg"
        style={{ minHeight: '240px' }}
      />
      
      {/* Loading overlay - positioned absolutely to not interfere with map DOM */}
      {!isLoaded && !mapError && (
        <div 
          className="absolute inset-0 bg-secondary_alt rounded-lg flex items-center justify-center z-10"
          style={{ pointerEvents: 'none' }}
        >
          <p className="text-tertiary">Loading map...</p>
        </div>
      )}
      
      {/* Error overlay - positioned absolutely to not interfere with map DOM */}
      {mapError && (
        <div 
          className="absolute inset-0 bg-secondary_alt rounded-lg flex items-center justify-center z-10"
          style={{ pointerEvents: 'auto' }}
        >
          <div className="text-center p-4">
            <p className="text-tertiary mb-2">{mapError}</p>
            <a 
              href={`https://maps.google.com/?q=${encodeURIComponent(address)}`}
              target="_blank"
              rel="noopener noreferrer"
              className="text-sm text-brand-secondary hover:underline"
            >
              Open in Google Maps
            </a>
          </div>
        </div>
      )}
    </div>
  );
}
