'use client';

import React, { createContext, useContext, ReactNode, useState, useEffect, useRef } from 'react';
import { GOOGLE_MAPS_LIBRARIES, GOOGLE_MAPS_ID } from '../map-config';

interface GoogleMapsContextType {
  isLoaded: boolean;
  loadError: Error | undefined;
  load: (apiKey: string) => Promise<void>;
}

const GoogleMapsContext = createContext<GoogleMapsContextType>({
  isLoaded: false,
  loadError: undefined,
  load: () => Promise.resolve(),
});

// Singleton global para prevenir múltiplos carregamentos
declare global {
  interface Window {
    __XERTICA_GOOGLE_MAPS_LOADER__?: {
      isLoaded: boolean;
      loadError: Error | undefined;
      listeners: Set<(state: GoogleMapsContextType) => void>;
      scriptElement?: HTMLScriptElement;
    };
  }
}

/**
 * Helper that synchronously reads the Google Maps API key from the environment or localStorage
 */
function getInitialApiKey(): string | undefined {
  // 1. Check Environment Variable (Vite)
  if (
    typeof import.meta !== 'undefined' &&
    import.meta.env &&
    import.meta.env.VITE_GOOGLE_MAPS_API_KEY
  ) {
    return import.meta.env.VITE_GOOGLE_MAPS_API_KEY as string;
  }

  if (typeof window === 'undefined') return undefined;

  const savedKey = window.localStorage?.getItem('xertica-googlemaps-api-key');

  if (savedKey && savedKey.trim().length > 0) {
    return savedKey;
  }

  return undefined;
}

/**
 * Removes the existing Google Maps script tag and clears the global Maps object
 */
function removeExistingScript(): void {
  if (typeof window === 'undefined') return;

  // Remover callback global se existir
  if ((window as any).__googleMapsCallback) {
    delete (window as any).__googleMapsCallback;
  }

  // Remover script existente
  const existingScript = document.querySelector(`script[src*="maps.googleapis.com/maps/api/js"]`);
  if (existingScript) {
    existingScript.remove();
  }

  // Limpar Google Maps do window
  if ((window as any).google?.maps) {
    delete (window as any).google.maps;
  }

  // Limpar singleton para permitir novo carregamento
  if (window.__XERTICA_GOOGLE_MAPS_LOADER__) {
    window.__XERTICA_GOOGLE_MAPS_LOADER__.isLoaded = false;
    window.__XERTICA_GOOGLE_MAPS_LOADER__.loadError = undefined;
    window.__XERTICA_GOOGLE_MAPS_LOADER__.scriptElement = undefined;
  }
}

/**
 * Returns true if the Google Maps JS API has already been loaded
 */
function isGoogleMapsAlreadyLoaded(): boolean {
  if (typeof window === 'undefined') return false;
  return window.google?.maps?.version !== undefined;
}

/**
 * Returns true if the AdvancedMarkerElement marker library is available
 */
function isMarkerLibraryLoaded(): boolean {
  if (typeof window === 'undefined') return false;
  return window.google?.maps?.marker?.AdvancedMarkerElement !== undefined;
}

/**
 * Dynamically injects the Google Maps JS script tag and resolves when all libraries are loaded
 */
function loadGoogleMapsScript(apiKey?: string): Promise<void> {
  return new Promise((resolve, reject) => {
    if (typeof window === 'undefined') {
      reject(new Error('Window is undefined'));
      return;
    }

    // Validar API key antes de carregar
    if (!apiKey || apiKey.length < 10) {
      reject(
        new Error(
          'Invalid or missing Google Maps API key. Please configure your API key in Settings.'
        )
      );
      return;
    }

    // Se já está carregado com a mesma key, resolver imediatamente
    if (isGoogleMapsAlreadyLoaded() && isMarkerLibraryLoaded()) {
      // Verificar se a API key atual é a mesma
      const existingScript = document.querySelector(
        `script[src*="maps.googleapis.com/maps/api/js"]`
      ) as HTMLScriptElement;
      if (existingScript && existingScript.src.includes(`key=${apiKey}`)) {
        resolve();
        return;
      }
    }

    // Verificar se o script já existe
    const existing = document.querySelector(
      `script[src*="maps.googleapis.com/maps/api/js"]`
    ) as HTMLScriptElement;
    if (existing) {
      // Se existe mas com chave diferente, logar aviso. Não podemos recarregar com SPA se componentes já foram definidos.
      if (!existing.src.includes(`key=${apiKey}`)) {
        console.warn(
          '[GoogleMapsLoader] API key changed, but Google Maps cannot be reloaded without a full page refresh because custom elements are already defined.'
        );
        // Don't reject, just resolve to keep the app working with the old key until refresh
        resolve();
        return;
      }

      // Aguardar o script carregar
      if (isGoogleMapsAlreadyLoaded() && isMarkerLibraryLoaded()) {
        resolve();
      } else {
        existing.addEventListener('load', () => {
          setTimeout(() => {
            if (isMarkerLibraryLoaded()) {
              resolve();
            } else {
              reject(new Error('Marker library failed to load'));
            }
          }, 100);
        });
        existing.addEventListener('error', () => reject(new Error('Failed to load Google Maps')));
      }
      return;
    }

    // Prevenir reinjeção se customElements já tem gmp-map (garantia final)
    if (typeof customElements !== 'undefined' && customElements.get('gmp-map')) {
      console.warn(
        '[GoogleMapsLoader] gmp-map is already defined in customElements. Skipping script injection.'
      );
      resolve();
      return;
    }

    // Criar novo script
    const script = document.createElement('script');
    const params = new URLSearchParams({
      key: apiKey, // SEMPRE incluir a API key
      callback: '__googleMapsCallback',
      libraries: GOOGLE_MAPS_LIBRARIES.join(','),
      v: 'quarterly', // Usar 'quarterly' para versão estável ao invés de 'weekly'
      loading: 'async',
    });

    script.src = `https://maps.googleapis.com/maps/api/js?${params.toString()}`;
    script.async = true;
    script.defer = true;
    script.id = GOOGLE_MAPS_ID;

    // Callback global
    (window as any).__googleMapsCallback = () => {
      delete (window as any).__googleMapsCallback;
      // Aguardar um pouco para garantir que todas as bibliotecas foram carregadas
      setTimeout(() => {
        if (isMarkerLibraryLoaded()) {
          resolve();
        } else {
          reject(new Error('Marker library failed to load'));
        }
      }, 100);
    };

    script.addEventListener('error', () => {
      delete (window as any).__googleMapsCallback;
      reject(
        new Error(
          'Failed to load Google Maps script. Please check your API key and ensure Maps JavaScript API is enabled.'
        )
      );
    });

    document.head.appendChild(script);

    // Salvar referência ao script
    const singleton = getOrCreateSingleton();
    if (singleton) {
      singleton.scriptElement = script;
    }
  });
}

/**
 * Inicializa o singleton global
 */
function getOrCreateSingleton() {
  if (typeof window === 'undefined') return null;

  if (!window.__XERTICA_GOOGLE_MAPS_LOADER__) {
    const isPreloaded = isGoogleMapsAlreadyLoaded();

    window.__XERTICA_GOOGLE_MAPS_LOADER__ = {
      isLoaded: isPreloaded,
      loadError: undefined,
      listeners: new Set(),
      scriptElement: undefined,
    };
  }

  return window.__XERTICA_GOOGLE_MAPS_LOADER__;
}

/**
 * Atualiza o estado do singleton e notifica listeners
 */
function updateSingleton(state: Partial<GoogleMapsContextType>) {
  const singleton = getOrCreateSingleton();
  if (!singleton) return;

  if (state.isLoaded !== undefined) singleton.isLoaded = state.isLoaded;
  if (state.loadError !== undefined) singleton.loadError = state.loadError;

  // Prepare safe state to notify
  const newState: GoogleMapsContextType = {
    isLoaded: singleton.isLoaded,
    loadError: singleton.loadError,
    load: async (apiKey: string) => {
      // Allow manual loading call via stored function in context if needed,
      // though typically we use the direct export or loadGoogleMapsScript
      if (singleton.isLoaded) return;
      try {
        await loadGoogleMapsScript(apiKey);
        updateSingleton({ isLoaded: true, loadError: undefined });
      } catch (error: any) {
        updateSingleton({ isLoaded: false, loadError: error });
        throw error;
      }
    },
  };

  singleton.listeners.forEach(listener => listener(newState));
}

/**
 * Internal component that subscribes to the singleton loader state
 */
const SingletonLoaderWrapper = ({ children }: { children: ReactNode }) => {
  const [state, setState] = useState<GoogleMapsContextType>(() => {
    const singleton = getOrCreateSingleton();

    // Default load function that triggers the script load
    const loadFn = async (apiKey: string) => {
      if (singleton?.isLoaded) return;
      try {
        await loadGoogleMapsScript(apiKey);
        updateSingleton({ isLoaded: true, loadError: undefined });
      } catch (error: any) {
        updateSingleton({ isLoaded: false, loadError: error });
        throw error;
      }
    };

    if (!singleton) return { isLoaded: false, loadError: undefined, load: loadFn };

    return {
      isLoaded: singleton.isLoaded,
      loadError: singleton.loadError,
      load: loadFn,
    };
  });

  useEffect(() => {
    const singleton = getOrCreateSingleton();
    if (!singleton) return;

    const listener = (newState: GoogleMapsContextType) => {
      setState(newState);
    };

    singleton.listeners.add(listener);

    // Sincronizar estado inicial e função de load
    listener({
      isLoaded: singleton.isLoaded,
      loadError: singleton.loadError,
      load: state.load,
    });

    return () => {
      singleton.listeners.delete(listener);
    };
  }, []);

  return <GoogleMapsContext.Provider value={state}>{children}</GoogleMapsContext.Provider>;
};

/**
 * Internal component that manually triggers the Google Maps script load
 */
const LoaderInitializer = ({ apiKey }: { apiKey?: string }) => {
  const hasInitializedRef = useRef(false);

  useEffect(() => {
    // Prevenir múltiplas execuções
    if (hasInitializedRef.current) {
      return;
    }

    const singleton = getOrCreateSingleton();
    if (!singleton) return;

    // Se já está carregado, atualizar estado
    if (isGoogleMapsAlreadyLoaded() && isMarkerLibraryLoaded()) {
      updateSingleton({ isLoaded: true, loadError: undefined });
      hasInitializedRef.current = true;
      return;
    }

    hasInitializedRef.current = true;

    // Use prop key OR get from storage
    const keyToUse = apiKey || getInitialApiKey();

    // Se não houver API key, apenas marcar como não carregado (sem erro)
    if (!keyToUse) {
      updateSingleton({
        isLoaded: false,
        loadError: undefined, // Não definir erro quando não há API key
      });
      return;
    }

    loadGoogleMapsScript(keyToUse)
      .then(() => {
        updateSingleton({ isLoaded: true, loadError: undefined });
      })
      .catch(error => {
        updateSingleton({ isLoaded: false, loadError: error });
      });
  }, [apiKey]);

  return null;
};

interface GoogleMapsLoaderProviderProps {
  children: ReactNode;
  apiKey?: string;
}

/**
 * GoogleMapsLoaderProvider
 *
 * Provider global que gerencia o carregamento da API do Google Maps.
 * Usa carregamento manual do script para evitar conflitos com custom elements.
 */
export const GoogleMapsLoaderProvider = ({ children, apiKey }: GoogleMapsLoaderProviderProps) => {
  const [shouldInitialize] = useState(() => {
    const singleton = getOrCreateSingleton();
    if (!singleton) return false;

    // Se já está carregado, não inicializar
    if (singleton.isLoaded || isGoogleMapsAlreadyLoaded() || isMarkerLibraryLoaded()) {
      singleton.isLoaded = true;
      return false;
    }

    return true;
  });

  return (
    <>
      {shouldInitialize && <LoaderInitializer apiKey={apiKey} />}
      <SingletonLoaderWrapper>{children}</SingletonLoaderWrapper>
    </>
  );
};

export const useGoogleMapsLoader = () => useContext(GoogleMapsContext);

/**
 * Recarrega o Google Maps com uma nova API key
 */
export function reloadGoogleMaps(newApiKey: string): Promise<void> {
  return new Promise((resolve, reject) => {
    if (typeof window === 'undefined') {
      reject(new Error('Window is undefined'));
      return;
    }

    // Validar API key
    if (!newApiKey || newApiKey.length < 10) {
      reject(new Error('Invalid or missing Google Maps API key'));
      return;
    }

    // Verificar se a key atual é a mesma
    const existingScript = document.querySelector(
      `script[src*="maps.googleapis.com/maps/api/js"]`
    ) as HTMLScriptElement;
    if (existingScript && existingScript.src.includes(`key=${newApiKey}`)) {
      // Mesma key, apenas resolver (ou esperar carregar)
      resolve();
      return;
    }

    // Se a chave for diferente, NÃO PODEMOS remover o script e adicionar outro
    // se o Google Maps já definiu custom elements. Isso causa o erro:
    // "Element with name 'gmp-...' already defined"
    if (typeof customElements !== 'undefined' && customElements.get('gmp-map')) {
      console.warn(
        '[GoogleMapsLoader] Cannot reload map API dynamically because custom elements (gmp-map) are already registered. A full page reload is required to apply the new API key.'
      );
      resolve(); // Resolver para não quebrar a UI
      return;
    }

    // Se chegou aqui e não tem custom elements, podemos tentar recarregar
    removeExistingScript();

    // Atualizar singleton
    updateSingleton({ isLoaded: false, loadError: undefined });

    // Carregar novo script
    loadGoogleMapsScript(newApiKey)
      .then(() => {
        updateSingleton({ isLoaded: true, loadError: undefined });
        resolve();
      })
      .catch(error => {
        updateSingleton({ isLoaded: false, loadError: error });
        reject(error);
      });
  });
}

// Alias for backwards compatibility
export const GoogleMapsProvider = GoogleMapsLoaderProvider;
