/* eslint-disable @typescript-eslint/no-namespace */

import React, { useRef, useEffect } from 'react';

import {
  Anchor,
  MapboxAddressMinimap,
  MapStyleMode,
  Theme
} from '@mapbox/search-js-web';

declare global {
  namespace JSX {
    interface IntrinsicElements {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      'mapbox-address-minimap': any;
    }
  }
}

/**
 * @typedef AddressMinimapProps
 */
export interface AddressMinimapProps {
  /**
   * If `true`, the marker can be moved around the map. Defaults to `false`.
   *
   * When editable, the marker can be moved around the map and the updated
   * location can be referenced from the {@link AddressMinimapProps#onSaveMarkerLocation} callback.
   */
  canAdjustMarker?: boolean;
  /**
   * If `true`, the map when panned moves around the marker, keeping the marker
   * centered. Defaults to `false`.
   */
  keepMarkerCentered?: boolean;
  /**
   * The anchor of the marker, relative to center of the expanded size. Defaults to `'bottom'`.
   */
  markerAnchor?: Anchor;
  /**
   * A client-defined callback that is triggered when the "Save" button is clicked in the editing interface,
   * and gives access to the adjusted marker coordinate.
   */
  onSaveMarkerLocation?: (coordinate: [number, number]) => void;

  /**
   * Must be `true` for the minimap to be shown, in addition to {@link AddressMinimapProps#feature}
   * being present.
   */
  show?: boolean;
  /**
   * The [Mapbox access token](https://docs.mapbox.com/help/glossary/access-token/) to use for all requests.
   *
   * If not explicitly set on the component, this will reference the value in the global config object.
   */
  accessToken?: string;
  /**
   * A [GeoJSON](https://docs.mapbox.com/help/glossary/geojson/) Feature representing
   * a [Point](https://geojson.org/geojson-spec.html#point) geometry.
   *
   * The minimap is hidden unless {@link AddressMinimapProps#feature} is truthy.
   */
  feature?: GeoJSON.Feature<GeoJSON.Point>;

  /**
   * If `true`, the map will have an image toggle between Map and Satellite styles.
   */
  satelliteToggle?: boolean;
  /**
   * The {@link Theme} to use for styling interface buttons.
   * @example
   * ```typescript
   * <AddressMinimap theme={{
   *   variables: {
   *     colorPrimary: 'myBrandRed'
   *   }
   * }}>
   * ```
   */
  theme?: Theme;
  /**
   * The map style to use, either `'default'` or `'satellite'`. The default map
   * style is configurable with {@link AddressMinimapProps#defaultMapStyle}.
   */
  mapStyleMode?: MapStyleMode;
  /**
   * The map style to use for the default map style. Defaults to `['mapbox', 'streets-v11']`.
   */
  defaultMapStyle?: [string, string];
  /**
   * Custom footer text appearing below the map, when marker adjustment is enabled.
   * If `true` or left undefined, the default footer text will be used.
   * If `false`, the footer will not be shown.
   */
  footer?: boolean | string;
  /**
   * Custom adjust button text appearing on the map.
   * If not provided, the default text will be used.
   */
  adjustBtnText?: string;
  /**
   * Custom cancel button text appearing on the map, when marker adjustment is enabled.
   * If not provided, the default text will be used.
   */
  cancelBtnText?: string;
  /**
   * Custom save button text appearing on the map, when marker adjustment is enabled.
   * If not provided, the default text will be used.
   */
  saveBtnText?: string;
}

/**
 * `AddressMinimap` is a React component that displays a marker for confirmation purposes.
 *
 * Optionally, this marker is editable. When editable, the marker can be moved
 * around the map and the updated location is sent back to the Mapbox Contribute
 * workflow.
 *
 * The goal of `AddressMinimap` is to reduce delivery or geolocation error in shipping and
 * local dispatching contexts.
 *
 * `AddressMinimap` expands to fill its container, and is hidden unless
 * {@link AddressMinimapProps#feature} and {@link AddressMinimapProps#show} are truthy.
 *
 * Internally, this wraps the [`<mapbox-address-minimap>`](https://docs.mapbox.com/mapbox-search-js/api/web/minimap/#mapboxaddressminimap) element.
 *
 * @class AddressMinimap
 * @param {AddressMinimapProps} props
 * @example
 * ```typescript
 * export function Component() {
 *   return (
 *     <AddressMinimap accessToken={'YOUR_MAPBOX_ACCESS_TOKEN'}>
 *     </AddressMinimap>
 *   );
 * }
 * ```
 */
export function AddressMinimap(props: AddressMinimapProps): React.ReactElement {
  const {
    canAdjustMarker = false,
    keepMarkerCentered = false,
    markerAnchor = 'bottom',
    onSaveMarkerLocation,

    show = false,
    accessToken,
    feature = null,

    adjustBtnText,
    saveBtnText,
    cancelBtnText,

    satelliteToggle = false,
    theme,
    mapStyleMode = 'default',
    defaultMapStyle = ['mapbox', 'streets-v11'],
    footer
  } = props;
  const ref = useRef<MapboxAddressMinimap>();

  // Update show.
  useEffect(() => {
    if (!ref.current) return;
    if (show) {
      ref.current.show();
    } else {
      ref.current.hide();
    }
  }, [ref.current, show]);

  // Update theme.
  useEffect(() => {
    if (ref.current) ref.current.theme = theme;
  }, [ref.current, theme]);

  // Update feature.
  useEffect(() => {
    if (ref.current) ref.current.feature = show ? feature : null;
  }, [ref.current, feature, show]);

  // Update mapStyleMode.
  useEffect(() => {
    if (ref.current) ref.current.mapStyleMode = mapStyleMode;
  }, [ref.current, mapStyleMode]);

  // Update adjustBtnText.
  useEffect(() => {
    if (adjustBtnText === undefined) return;
    if (ref.current) ref.current.adjustBtnText = adjustBtnText;
  }, [ref.current, adjustBtnText]);

  // Update saveBtnText.
  useEffect(() => {
    if (saveBtnText === undefined) return;
    if (ref.current) ref.current.saveBtnText = saveBtnText;
  }, [ref.current, saveBtnText]);

  // Update cancelBtnText.
  useEffect(() => {
    if (cancelBtnText === undefined) return;
    if (ref.current) ref.current.cancelBtnText = cancelBtnText;
  }, [ref.current, cancelBtnText]);

  // Update defaultMapStyle.
  useEffect(() => {
    if (ref.current) ref.current.defaultMapStyle = defaultMapStyle;
  }, [ref.current, defaultMapStyle]);

  // Update footer.
  useEffect(() => {
    if (footer === undefined) return;
    if (ref.current) ref.current.footer = footer;
  }, [ref.current, footer]);

  // Update accessToken.
  useEffect(() => {
    if (ref.current) ref.current.accessToken = accessToken;
  }, [ref.current, accessToken]);

  // Update onSaveMarkerLocation callback
  useEffect(() => {
    if (ref.current) ref.current.onSaveMarkerLocation = onSaveMarkerLocation;
  }, [ref.current, onSaveMarkerLocation]);

  return (
    <mapbox-address-minimap
      ref={ref}
      can-adjust-marker={canAdjustMarker}
      keep-marker-centered={keepMarkerCentered}
      marker-anchor={markerAnchor}
      satellite-toggle={satelliteToggle}
    />
  );
}
