/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import { createEventHandle } from '../createEventHandle/index'
import { useLayoutEffectImpl as useLayoutEffect } from '../useLayoutEffect/index'
import { useStable } from '../useStable/index'

type Callback = null | ((arg0: any) => void)
type AddListener = (
  target: EventTarget,
  listener: null | ((arg0: any) => void)
) => () => void

/**
 * This can be used with any event type include custom events.
 *
 * const click = useEvent('click', options);
 * useEffect(() => {
 *   click.setListener(target, onClick);
 *   return () => click.clear();
 * }).
 */
export function useEvent(
  event: string,
  options?: {
    capture?: boolean
    passive?: boolean
  } | null
): AddListener {
  const targetListeners = useStable(() => new Map())

  const addListener = useStable(() => {
    const addEventListener = createEventHandle(event, options)
    return (target: EventTarget, callback: Callback) => {
      const removeTargetListener = targetListeners.get(target)
      if (removeTargetListener != null) {
        removeTargetListener()
      }
      if (callback == null) {
        targetListeners.delete(target)
      }
      const removeEventListener = addEventListener(target, callback)
      targetListeners.set(target, removeEventListener)
      return removeEventListener
    }
  })

  useLayoutEffect(() => {
    return () => {
      targetListeners.forEach((removeListener) => {
        removeListener()
      })
      targetListeners.clear()
    }
  }, [targetListeners])

  return addListener
}
