import React, {
  useState,
  useContext,
  useRef,
  useEffect,
  useMemo,
  ReactNode,
  createContext,
  useCallback
} from 'react';
import maplibre, { Map, NavigationControl } from 'maplibre-gl';
import type { Style } from 'maplibre-gl';

import invariant from 'tiny-invariant';

import { useStyle, useElementVisible } from './hooks';
import { StyleControl } from './StyleControl';

const Context = createContext<{ map?: Map | null }>({});

type MapLibreProps = {
  layers: string[];
  children: ReactNode;
};

export function useMapLibre() {
  const context = useContext(Context);
  invariant(context.map, 'Maplibre not initialized');
  return context.map;
}

export function MapLibre({ children, layers }: MapLibreProps) {
  const isSupported = useMemo(
    () => maplibre.supported({ failIfMajorPerformanceCaveat: true }) && !isIE(),
    []
  );
  const containerRef = useRef<HTMLDivElement>(null);
  const visible = useElementVisible(containerRef);
  const [map, setMap] = useState<Map | null>();

  const onStyleChange = useCallback(
    (style: Style) => {
      if (map) {
        map.setStyle(style);
      }
    },
    [map]
  );
  const { style, ...mapStyleProps } = useStyle(layers, onStyleChange);

  useEffect(() => {
    if (isSupported && visible && !map) {
      invariant(containerRef.current, 'Map container not found');
      const map = new Map({
        container: containerRef.current,
        style
      });
      map.addControl(new NavigationControl({}), 'top-right');
      map.on('load', () => {
        setMap(map);
      });
    }
  }, [map, style, visible, isSupported]);

  if (!isSupported) {
    return (
      <div
        style={{ marginBottom: '20px' }}
        className="outdated-browser-banner site-banner"
      >
        <div className="container">
          <div className="site-banner-icon">⚠️</div>
          <div className="site-banner-text">
            Nous ne pouvons pas afficher la carte car elle est imcompatible avec
            votre navigateur. Nous vous conseillons de le mettre à jour ou
            d’utiliser{' '}
            <a
              href="https://browser-update.org/fr/update.html"
              target="_blank"
              rel="noopener noreferrer"
            >
              un navigateur plus récent
            </a>
            .
          </div>
        </div>
      </div>
    );
  }

  return (
    <Context.Provider value={{ map }}>
      <div ref={containerRef} style={{ height: '500px' }}>
        <StyleControl styleId={style.id} {...mapStyleProps} />
        {map ? children : null}
      </div>
    </Context.Provider>
  );
}

function isIE() {
  const ua = window.navigator.userAgent;
  const msie = ua.indexOf('MSIE ');
  const trident = ua.indexOf('Trident/');
  return msie > 0 || trident > 0;
}