import { useEffect } from 'react'
import { useMapStore } from './providers/map/provider'
import type { MapComponentProps } from './types'
import { MAPS_BASEMAP, MapsTypes } from './const'
import {
  type RecognizerTuple,
  EventManager,
  Pan,
  InputDirection,
  Pinch,
  Tap,
} from 'mjolnir.js'
import type { Layer } from './providers/layers/types'
import { useLayerStore } from './providers/layers/store'
import type { LayerProps } from '@deck.gl/core'

export const EVENT_TYPES = [
  'click',
  'keyup',
  'panstart',
  'panmove',
  'panend',
  'pointermove',
] as const

const RECOGNIZERS = {
  multipan: [
    Pan,
    { threshold: 10, direction: InputDirection.Vertical, pointers: 2 },
  ],
  pinch: [Pinch, {}, null, ['multipan']],
  click: [Tap, { event: 'click' }, null, ['dblclick']],
  dblclick: [Tap, { event: 'dblclick', taps: 2 }],
  pan: [Pan, { threshold: 1 }, ['pinch'], ['multipan']],
} as const

/**
 * Creates and attaches an mjolnir.js `EventManager` to a layer for handling pointer and gesture events on Google Maps basemaps.
 *
 * @param mapId - Identifier of the map instance.
 * @param layerId - Identifier of the layer to attach the event manager to.
 *
 * @remarks
 * The event manager is only created when using a Google Maps basemap. For MapLibre basemaps, no additional event manager is needed.
 */
export function useEventManager({
  mapId,
  layerId,
}: {
  mapId: MapComponentProps['id']
  layerId: NonNullable<Layer['props']['id']>
}) {
  const basemap = useMapStore((store) => store.maps[mapId]?.basemap)!
  const isLoaded = useMapStore((store) => store.maps[mapId]?.isLoaded)
  const getOverlayInstance = useMapStore((store) => store.getOverlayInstance)
  const setLayer = useLayerStore((state) => state.setLayer)

  const { type } = MAPS_BASEMAP[basemap]
  const isGmaps = type === MapsTypes.googleMaps

  useEffect(() => {
    let eventManager: EventManager | null = null

    if (isGmaps && isLoaded) {
      const overlayInstance = getOverlayInstance(mapId) as google.maps.Map
      eventManager = new EventManager(overlayInstance.getDiv(), {
        recognizers: Object.keys(RECOGNIZERS).map((eventName: string) => {
          const [
            RecognizerConstructor,
            defaultOptions,
            recognizeWith,
            requestFailure,
          ] = RECOGNIZERS[eventName as keyof typeof RECOGNIZERS]

          const options = { ...defaultOptions, event: eventName }

          return {
            recognizer: new RecognizerConstructor(options),
            recognizeWith,
            requestFailure,
          } as RecognizerTuple
        }),
      })
    }

    setLayer(layerId, {
      props: {
        eventManager,
      } as LayerProps & {
        eventManager: EventManager | null
      },
    })

    return () => {
      eventManager?.destroy()
      eventManager = null
    }
  }, [getOverlayInstance, isGmaps, isLoaded, layerId, mapId, setLayer])
}
