import React from 'react';

import cx from 'classnames';

import {
  Map,
  Layer,
  Layers,
  Controls,
  useMapContext,
} from '@eeacms/volto-openlayers-map/api';
import { withOpenLayers } from '@eeacms/volto-openlayers-map';
import { GISCO_OSM_ATTRIBUTION } from '@eeacms/volto-bise-policy/constants';

import InfoOverlay from './InfoOverlay';
import FeatureInteraction from './FeatureInteraction';
import CaseStudyList from './CaseStudyListing';

import {
  centerAndResetMapZoom,
  CLUSTER_COLOR,
  getFeatures,
  getSelectInteraction,
  scrollToElement,
} from './utils';

const styleCache = {};
const MapContextGateway = ({ setMap }) => {
  const { map } = useMapContext();
  React.useEffect(() => {
    setMap(map);
  }, [map, setMap]);
  return null;
};

function CaseStudyMap(props) {
  const {
    items,
    activeItems,
    hideFilters,
    selectedCase,
    onSelectedCase,
    searchInput,
    map,
    setMap,
    ol,
  } = props;
  const features = getFeatures({ cases: items, ol });
  const [resetMapButtonClass, setResetMapButtonClass] =
    React.useState('inactive');

  const [tileWMSSources] = React.useState([
    new ol.source.TileWMS({
      url: 'https://gisco-services.ec.europa.eu/maps/service',
      attributions: GISCO_OSM_ATTRIBUTION,
      params: {
        // LAYERS: 'OSMBlossomComposite', OSMCartoComposite, OSMPositronComposite
        LAYERS: 'OSMPositronComposite',
        TILED: true,
      },
      serverType: 'geoserver',
      transition: 0,
    }),
  ]);
  const [pointsSource] = React.useState(
    new ol.source.Vector({
      features,
    }),
  );

  const [clusterSource] = React.useState(
    new ol.source.Cluster({
      distance: 19,
      source: pointsSource,
    }),
  );

  // `ol` is a fresh object literal on every render (see withOpenLayers); keep
  // the latest reference in a ref so effects can depend only on values that
  // actually change. Refreshing the points source regenerates the cluster
  // features, which drops the currently selected feature from the rendered set.
  const olRef = React.useRef(ol);
  olRef.current = ol;

  React.useEffect(() => {
    if (activeItems) {
      pointsSource.clear();
      pointsSource.addFeatures(
        getFeatures({ cases: activeItems, ol: olRef.current }),
      );
    }
  }, [activeItems, pointsSource]);

  React.useEffect(() => {
    if (!map) return;

    const moveendListener = () => {
      const mapZoom = Math.round(map.getView().getZoom() * 10) / 10;
      const mapCenter = map.getView().getCenter();
      const selectInteraction = getSelectInteraction(map);

      if (selectInteraction) {
        if (selectedCase) {
          const coords = selectedCase.geometry.flatCoordinates;
          const pixel = map.getPixelFromCoordinate(coords);
          const selectedFeature = map.getFeaturesAtPixel(pixel)[0];

          selectInteraction.getFeatures().clear();
          if (selectedFeature) {
            selectInteraction.getFeatures().push(selectedFeature);
          }
        } else {
          selectInteraction.getFeatures().clear();
        }
      }

      if (
        mapZoom === 4 &&
        JSON.stringify(mapCenter) ===
          JSON.stringify(ol.proj.transform([10, 49], 'EPSG:4326', 'EPSG:3857'))
      ) {
        setResetMapButtonClass('inactive');
      } else {
        setResetMapButtonClass('active');
      }
    };

    map.on('moveend', moveendListener);

    return () => {
      map.un('moveend', moveendListener);
    };
  }, [map, selectedCase, ol.proj, setResetMapButtonClass]);

  const clusterStyle = React.useMemo(() => selectedClusterStyle({ ol }), [ol]);

  const MapWithSelection = React.useMemo(() => Map, []);
  // console.log('render');

  return features.length > 0 ? (
    <div id="ol-map-container" className="nrr-case-study-map-container">
      <MapWithSelection
        view={{
          center: ol.proj.fromLonLat([10, 49]),
          showFullExtent: true,
          zoom: 4,
        }}
        pixelRatio={1}
      >
        <Controls
          attribution={true}
          attributionOptions={{ collapsible: false }}
        />
        <Layers>
          {hideFilters ? null : (
            <button
              className={cx(
                'reset-map-button ui button secondary',
                String(resetMapButtonClass),
              )}
              onClick={() => {
                scrollToElement('search-input');
                onSelectedCase(null);
                centerAndResetMapZoom({ map, ol });
                getSelectInteraction(map)?.getFeatures().clear();
              }}
            >
              <span className="result-info-title">Reset map</span>
              <i className="icon ri-map-2-line"></i>
            </button>
          )}
          <InfoOverlay
            selectedFeature={selectedCase}
            onFeatureSelect={onSelectedCase}
            layerId={tileWMSSources[0]}
            hideFilters={hideFilters}
          />
          <FeatureInteraction
            onFeatureSelect={onSelectedCase}
            hideFilters={hideFilters}
          />
          <Layer.Tile source={tileWMSSources[0]} zIndex={0} />
          <Layer.Vector
            style={clusterStyle}
            source={clusterSource}
            zIndex={1}
          />
          <MapContextGateway setMap={setMap} />
        </Layers>
      </MapWithSelection>
      {hideFilters ? null : (
        <>
          <div
            className="case-study-status-legend"
            aria-label="Case study status legend"
          >
            <span className="legend-title">Current status</span>
            {[
              ['planned', '#006bb8'],
              ['ongoing', '#ff9933'],
              ['completed', '#00a390'],
            ].map(([status, color]) => (
              <span className="legend-item" key={status}>
                <span
                  className="legend-dot"
                  style={{ backgroundColor: color }}
                />
                {status}
              </span>
            ))}
          </div>
          <CaseStudyList
            map={map}
            activeItems={activeItems}
            selectedCase={selectedCase}
            onSelectedCase={onSelectedCase}
            pointsSource={pointsSource}
            searchInput={searchInput}
          />
        </>
      )}
    </div>
  ) : null;
}

const selectedClusterStyle = ({ ol }) => {
  function _clusterStyle(feature) {
    const size = feature.get('features').length;
    let style = styleCache[size];

    if (!style) {
      style = new ol.style.Style({
        image: new ol.style.Circle({
          radius: 12 + Math.min(Math.floor(size / 3), 10),
          stroke: new ol.style.Stroke({
            color: '#fff',
          }),
          fill: new ol.style.Fill({
            color: CLUSTER_COLOR,
          }),
        }),
        text: new ol.style.Text({
          text: size.toString(),
          fill: new ol.style.Fill({
            color: '#fff',
          }),
        }),
      });
      styleCache[size] = style;
    }

    if (size === 1) {
      const color = feature.values_.features[0].get('color');

      return new ol.style.Style({
        image: new ol.style.Circle({
          radius: 6,
          fill: new ol.style.Fill({
            color: '#fff',
          }),
          stroke: new ol.style.Stroke({
            color: color,
            width: 6,
          }),
        }),
      });
    } else {
      return style;
    }
  }
  return _clusterStyle;
};

export default withOpenLayers(CaseStudyMap);
