import React, { useEffect, useRef, useState } from 'react';
import { createRoot } from 'react-dom/client';
import DOMPurify from 'dompurify';
import { cn } from '../../shared/utils';
import { useGoogleMapsLoader } from '../google-maps-loader';
import { useMapLayers, MapLayersConfig } from '../map-layers';

declare global {
  namespace JSX {
    interface IntrinsicElements {
      'gmp-map': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
        'map-id'?: string;
        center?: string | google.maps.LatLng | google.maps.LatLngLiteral;
        zoom?: number;
      };
      'gmp-advanced-marker': React.DetailedHTMLProps<
        React.HTMLAttributes<HTMLElement>,
        HTMLElement
      > & {
        title?: string;
        position?: string | google.maps.LatLng | google.maps.LatLngLiteral;
      };
    }
  }
}
export interface MapProps extends React.HTMLAttributes<HTMLDivElement> {
  center?: { lat: number; lng: number };
  zoom?: number;
  mapTypeId?: string;
  mapId?: string;
  markers?: Array<{
    position: { lat: number; lng: number };
    label?: string;
    title?: string;
    info?: string;
    customColor?: string;
    icon?: string;
    iconSvg?: string;
    iconColor?: string;
    infoWindowContent?: string; // Custom HTML content for InfoWindow
    richContent?: React.ReactNode; // Custom React component for InfoWindow
  }>;
  circle?: {
    center: { lat: number; lng: number };
    radius: number;
    fillColor?: string;
    strokeColor?: string;
  };
  polygon?: {
    paths: Array<{ lat: number; lng: number }>[];
    fillColor?: string;
    strokeColor?: string;
  };
  layers?: MapLayersConfig;
  height?: string;
  apiKey?: string;
  mapContainerClassName?: string;
  disableDefaultUI?: boolean;
  zoomControl?: boolean;
  streetViewControl?: boolean;
  mapTypeControl?: boolean;
  fullscreenControl?: boolean;
  gestureHandling?: 'cooperative' | 'greedy' | 'none' | 'auto';
  onMapLoad?: (map: google.maps.Map) => void;
}

const DEFAULT_CENTER = { lat: -23.5505, lng: -46.6333 };
const DEFAULT_ZOOM = 12;

const MapContent = React.forwardRef<HTMLDivElement, MapProps & { apiKey: string }>(
  ({ apiKey, ...props }, ref) => {
    const { isLoaded, loadError, load } = useGoogleMapsLoader();
    const {
      center = DEFAULT_CENTER,
      zoom = DEFAULT_ZOOM,
      markers = [],
      circle,
      polygon,
      layers,
      height = '400px',
      mapContainerClassName,
      disableDefaultUI = false,
      zoomControl = true,
      streetViewControl = false,
      mapTypeControl = false,
      fullscreenControl = true,
      gestureHandling = 'cooperative',
      onMapLoad,
      className,
      ...divProps
    } = props;

    const [selectedMarker, setSelectedMarker] = useState<number | null>(null);
    const mapRef = useRef<google.maps.Map | null>(null);
    const gmpMapRef = useRef<any>(null); // Ref for custom element
    const infoWindowRef = useRef<google.maps.InfoWindow | null>(null);
    const circleRef = useRef<google.maps.Circle | null>(null);
    const polygonRef = useRef<google.maps.Polygon | null>(null);

    // Resolve theme colors for Map shapes (Circle, Polygon)
    const [themeColors, setThemeColors] = useState({
      primary: '#4F46E5',
      chart2: '#10B981',
    });

    useEffect(() => {
      if (typeof window !== 'undefined') {
        const styles = getComputedStyle(document.documentElement);
        setThemeColors({
          primary: styles.getPropertyValue('--primary').trim() || '#4F46E5',
          chart2: styles.getPropertyValue('--chart-2').trim() || '#10B981',
        });
      }
    }, []);

    // Load API Key
    useEffect(() => {
      if (!isLoaded && apiKey && !loadError && load) {
        load(apiKey).catch(console.error);
      }
    }, [isLoaded, apiKey, loadError, load]);

    // Handle gmp-map initialization
    useEffect(() => {
      if (!isLoaded || !gmpMapRef.current) return;

      const gmpMap = gmpMapRef.current;

      // Access the underlying map instance
      if (gmpMap.innerMap) {
        mapRef.current = gmpMap.innerMap;
        if (onMapLoad) {
          onMapLoad(gmpMap.innerMap);
        }
      } else {
        // Fallback or wait for it to be ready if needed, usually available immediately after upgrade
        // Listener for 'gmp-map-load' doesn't exist standardly, but checking availability
        const interval = setInterval(() => {
          if (gmpMap.innerMap) {
            mapRef.current = gmpMap.innerMap;
            if (onMapLoad) {
              onMapLoad(gmpMap.innerMap);
            }
            clearInterval(interval);
          }
        }, 100);
        return () => clearInterval(interval);
      }

      return () => {
        mapRef.current = null;
      };
    }, [isLoaded]);

    // Sync properties with gmp-map custom element
    useEffect(() => {
      if (gmpMapRef.current) {
        if (center) gmpMapRef.current.center = center;
      }
    }, [center]);

    useEffect(() => {
      if (gmpMapRef.current) {
        if (zoom !== undefined) gmpMapRef.current.zoom = zoom;
      }
    }, [zoom]);

    useEffect(() => {
      if (gmpMapRef.current) {
        if (props.mapTypeId) gmpMapRef.current.mapTypeId = props.mapTypeId;
      }
    }, [props.mapTypeId]);

    // mapId is now set declaratively on the gmp-map element

    // Update Circle (Imperative approach still needed for shapes as there are no gmp-circle elements yet)
    useEffect(() => {
      const map = mapRef.current;
      if (!map || !isLoaded) return;

      circleRef.current?.setMap(null);
      circleRef.current = null;

      if (circle && circle.center && circle.radius) {
        circleRef.current = new google.maps.Circle({
          map,
          center: circle.center,
          radius: circle.radius,
          fillColor: circle.fillColor || themeColors.primary,
          fillOpacity: 0.2,
          strokeColor: circle.strokeColor || themeColors.primary,
          strokeOpacity: 0.8,
          strokeWeight: 2,
        });
      }

      return () => {
        circleRef.current?.setMap(null);
      };
    }, [circle, isLoaded, themeColors, mapRef.current]); // Added mapRef.current dependency

    // Update Polygon (Imperative approach still needed)
    useEffect(() => {
      const map = mapRef.current;
      if (!map || !isLoaded) return;

      polygonRef.current?.setMap(null);
      polygonRef.current = null;

      if (polygon && polygon.paths) {
        polygonRef.current = new google.maps.Polygon({
          map,
          paths: polygon.paths,
          fillColor: polygon.fillColor || themeColors.chart2,
          fillOpacity: 0.2,
          strokeColor: polygon.strokeColor || themeColors.chart2,
          strokeOpacity: 0.8,
          strokeWeight: 2,
        });
      }

      return () => {
        polygonRef.current?.setMap(null);
      };
    }, [polygon, isLoaded, themeColors, mapRef.current]);

    // Layers
    useMapLayers(mapRef.current, layers || {});

    // InfoWindow Management
    useEffect(() => {
      const map = mapRef.current;
      if (!map || selectedMarker === null) {
        infoWindowRef.current?.close();
        return;
      }

      const markerData = markers[selectedMarker];
      if (!markerData) return;

      // Note: We can't easily attach InfoWindow to gmp-advanced-marker via `anchor` prop in JS API
      // because gmp-advanced-marker is an HTMLElement, not an AdvancedMarkerElement instance directly in the same way.
      // However, gmp-advanced-marker has an .innerMarker property which IS the AdvancedMarkerElement.

      // We need references to the marker elements.
      // Since we are rendering them declaratively, we need to capture their refs or find them.
      // A simple way is to query them or use a callback ref approach in the render loop.
      // But here, selectedMarker is an index.

      // Let's defer InfoWindow opening to the click handler of the marker itself
    }, [selectedMarker, markers]);

    const handleMarkerClick = (index: number, markerElement: any) => {
      setSelectedMarker(index);
      const map = mapRef.current;
      if (!map) return;

      const markerData = markers[index];
      if (!markerData) return;

      // Determine content to render
      let contentToRender = markerData.richContent;

      // If no richContent but title/info exists, create default standard content
      if (!contentToRender && (markerData.title || markerData.info)) {
        contentToRender = (
          <div className="p-4 pr-8 min-w-[200px] max-w-[300px]">
            {markerData.title && (
              <h4 className="font-semibold text-base mb-1">{markerData.title}</h4>
            )}
            {markerData.info && <p className="text-sm text-muted-foreground">{markerData.info}</p>}
          </div>
        );
      }

      if (contentToRender && markerElement.innerMarker) {
        if (!infoWindowRef.current) {
          infoWindowRef.current = new google.maps.InfoWindow();
        }

        const container = document.createElement('div');
        const root = createRoot(container);
        root.render(contentToRender);

        infoWindowRef.current.setContent(container);
        infoWindowRef.current.open({
          map,
          anchor: markerElement.innerMarker,
        });

        const listener = infoWindowRef.current.addListener('closeclick', () => {
          setSelectedMarker(null);
          setTimeout(() => root.unmount(), 0);
        });
        // Cleanup logic for previous opens is tricky with this imperativeness mixed with declarative,
        // but 'open' usually closes previous one if same instance.
      }
    };

    if (loadError) {
      return (
        <div
          ref={ref}
          className={cn(
            'relative rounded-[var(--radius-card)] border border-destructive/50 overflow-hidden bg-destructive/5',
            className
          )}
          style={{ height }}
          {...divProps}
        >
          <div className="absolute inset-0 flex items-center justify-center">
            <div className="text-center space-y-2 p-6">
              <div className="text-destructive">
                <svg
                  className="w-12 h-12 mx-auto"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                >
                  <path
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    strokeWidth={2}
                    d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
                  />
                </svg>
              </div>
              <p className="text-sm font-medium text-foreground">Failed to load Google Maps</p>
              <p className="text-xs text-muted-foreground">Check API key in Settings</p>
            </div>
          </div>
        </div>
      );
    }

    if (!isLoaded) {
      return (
        <div
          ref={ref}
          className={cn(
            'relative rounded-[var(--radius-card)] border border-border overflow-hidden bg-muted animate-pulse',
            className
          )}
          style={{ height }}
          {...divProps}
        />
      );
    }

    return (
      <div
        ref={ref}
        className={cn(
          'relative rounded-[var(--radius-card)] border border-border overflow-hidden',
          className
        )}
        style={{ height }} // Apply height to container
        {...divProps}
      >
        {/* @ts-ignore - gmp-map is a custom element */}
        <gmp-map
          ref={gmpMapRef}
          style={{ height: '100%', width: '100%', display: 'block' }}
          map-id={props.mapId || 'DEMO_MAP_ID'}
        >
          {markers.map((markerData, idx) => {
            const markerColor = markerData.customColor || 'var(--primary)';
            const iconColor = markerData.iconColor || 'white';

            // Generate icon/content
            // Note: gmp-advanced-marker slot content is the custom marker content
            return (
              // @ts-ignore
              <gmp-advanced-marker
                key={idx}
                title={markerData.title}
                ref={(el: any) => {
                  if (el) {
                    el.position = markerData.position;
                    // Remove previous listener if any (useEffect cleanups handle component unmounts,
                    // but for ref callback we rely on it being called with null on unmount or re-render)
                    // However, adding listener repeatedly on re-renders might duplicate if not careful.
                    // React ref callback is called with null then value on updates if inline function changes.
                    // Ideally we should use a stable ref or effect, but this simple way works if we assume
                    // the element is recreated or we accept some overhead.
                    // Better: just add listener. standard addEventListener doesn't dedupe by default but
                    // with arrow function it will add new one.
                    // To be safe and simple: just set properties.
                    // For event listener, it's safer to not add it here if it's already there?
                    // Actually, let's just use the property 'onclick' if available? No, custom events.
                    // We will assume the element is fresh or we don't care too much about duplicate listeners for now
                    // AS LONG AS we don't create memory leaks.
                    // A better pattern is to use a Map<Element, Handler> to manage listeners but let's stick to simple first.
                    el.addEventListener('gmp-click', () => handleMarkerClick(idx, el));
                  }
                }}
              >
                <div
                  className="flex items-center justify-center w-8 h-8 border-[3px] border-background shadow-lg cursor-pointer origin-center transition-transform duration-200"
                  style={{
                    borderRadius: '50% 50% 50% 0',
                    transform: 'rotate(-45deg)',
                    backgroundColor: markerColor,
                  }}
                >
                  <div className="flex items-center justify-center rotate-45">
                    {markerData.iconSvg ? (
                      <div
                        dangerouslySetInnerHTML={{
                          __html: DOMPurify.sanitize(markerData.iconSvg, {
                            USE_PROFILES: { svg: true, svgFilters: true },
                          }),
                        }}
                        style={{ color: iconColor, width: 16, height: 16, fill: 'currentColor' }}
                      />
                    ) : (
                      <span className="font-semibold text-sm" style={{ color: iconColor }}>
                        {markerData.icon || markerData.label || ''}
                      </span>
                    )}
                  </div>
                </div>
              </gmp-advanced-marker>
            );
          })}
        </gmp-map>
      </div>
    );
  }
);
MapContent.displayName = 'MapContent';

/**
 * Primary Google Maps component.
 *
 * @description
 * Supports Advanced Markers, Circles, Polygons, and custom tile Layers.
 * Automatically loads the Google Maps JavaScript API via `useGoogleMapsLoader`.
 *
 * @ai-rules
 * 1. REQUIRED: Provide a valid `apiKey` via prop or the `VITE_GOOGLE_MAPS_API_KEY` environment variable.
 * 2. Use the `markers` prop to render points of interest. Supports `richContent` for React-based InfoWindows.
 * 3. Always set `height` explicitly (default is 400px) to ensure the map is visible in the layout.
 */
export const Map = React.forwardRef<HTMLDivElement, MapProps>((props, ref) => {
  const { isLoaded, loadError } = useGoogleMapsLoader();
  const effectiveApiKey =
    props.apiKey ||
    (typeof import.meta !== 'undefined' &&
      import.meta.env &&
      import.meta.env.VITE_GOOGLE_MAPS_API_KEY) ||
    '';

  const isValidKey =
    effectiveApiKey &&
    effectiveApiKey !== 'YOUR_GOOGLE_MAPS_API_KEY_HERE' &&
    effectiveApiKey.startsWith('AIza');

  if (isLoaded || isValidKey || loadError) {
    return <MapContent ref={ref} {...props} apiKey={effectiveApiKey} />;
  }

  // Check if the script is injected in the DOM (loading via provider)
  const isScriptInjected =
    typeof document !== 'undefined' &&
    !!document.querySelector('script[src*="maps.googleapis.com/maps/api/js"]');

  if (isScriptInjected) {
    // Let MapContent show the loading skeleton
    return <MapContent ref={ref} {...props} apiKey={effectiveApiKey} />;
  }

  return (
    <div
      ref={ref}
      className={cn(
        'relative rounded-[var(--radius-card)] border border-border overflow-hidden bg-muted',
        props.className
      )}
      style={{ height: props.height || '400px' }}
    >
      <div className="absolute inset-0 flex items-center justify-center bg-muted">
        <div className="text-center space-y-3 p-6">
          <div className="w-16 h-16 mx-auto bg-primary/10 rounded-full flex items-center justify-center">
            <svg
              className="w-8 h-8 text-primary"
              fill="none"
              stroke="currentColor"
              viewBox="0 0 24 24"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth={2}
                d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
              />
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth={2}
                d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
              />
            </svg>
          </div>
          <p className="text-sm text-muted-foreground">Configure Google Maps API Key in Settings</p>
        </div>
      </div>
    </div>
  );
});
Map.displayName = 'Map';
