import { useCallback, useRef, useEffect, useMemo } from 'react';
import mapboxgl, { Popup } from 'mapbox-gl';

import {
  filterFeatureCollection,
  findFeature,
  getBounds,
  getCenter
} from '../shared/mapbox/utils';

const SOURCE_CADASTRE = 'cadastre';

export function useMapbox(featureCollection) {
  const mapRef = useRef();
  const selectedCadastresRef = useRef(() => new Set());
  const isSupported = useMemo(() => mapboxgl.supported());

  const fitBounds = useCallback((bbox) => {
    mapRef.current.fitBounds(bbox, { padding: 100 });
  }, []);

  const onLoad = useCallback(
    (map) => {
      if (!mapRef.current) {
        mapRef.current = map;
        mapRef.current.fitBounds(featureCollection.bbox, { padding: 100 });
        onStyleChange();
      }
    },
    [featureCollection]
  );

  const onStyleChange = useCallback(() => {
    if (mapRef.current) {
      selectedCadastresRef.current = new Set(
        filterFeatureCollection(
          featureCollection,
          SOURCE_CADASTRE
        ).features.map(({ properties }) => properties.cid)
      );
      if (selectedCadastresRef.current.size > 0) {
        mapRef.current.setFilter('parcelle-highlighted', [
          'in',
          'id',
          ...selectedCadastresRef.current
        ]);
      }
    }
  }, [featureCollection]);

  const popup = useMemo(
    () =>
      new Popup({
        closeButton: false,
        closeOnClick: false
      })
  );

  const onMouseEnter = useCallback(
    (event) => {
      const feature = event.features[0];
      if (feature.properties && feature.properties.description) {
        const coordinates = getCenter(feature.geometry, event.lngLat);
        const description = feature.properties.description;
        mapRef.current.getCanvas().style.cursor = 'pointer';
        popup.setLngLat(coordinates).setHTML(description).addTo(mapRef.current);
      } else {
        popup.remove();
      }
    },
    [popup]
  );

  const onMouseLeave = useCallback(() => {
    mapRef.current.getCanvas().style.cursor = '';
    popup.remove();
  }, [popup]);

  useExternalEvents(featureCollection, { fitBounds });

  return { isSupported, onLoad, onStyleChange, onMouseEnter, onMouseLeave };
}

function useExternalEvents(featureCollection, { fitBounds }) {
  const onFeatureFocus = useCallback(
    ({ detail }) => {
      const { id } = detail;
      const feature = findFeature(featureCollection, id);
      if (feature) {
        fitBounds(getBounds(feature.geometry));
      }
    },
    [featureCollection, fitBounds]
  );

  useEvent('map:feature:focus', onFeatureFocus);
}

export function useEvent(eventName, callback) {
  return useEffect(() => {
    addEventListener(eventName, callback);
    return () => removeEventListener(eventName, callback);
  }, [eventName, callback]);
}