import {createContext, useContext, useMemo, useRef, useState} from 'react'
import {View} from 'react-native'
import {
  Gesture,
  GestureDetector,
  type GestureStateChangeEvent,
  type GestureUpdateEvent,
  type PanGestureHandlerEventPayload,
} from 'react-native-gesture-handler'
import EventEmitter from 'eventemitter3'

export type GlobalGestureEvents = {
  begin: GestureStateChangeEvent<PanGestureHandlerEventPayload>
  update: GestureUpdateEvent<PanGestureHandlerEventPayload>
  end: GestureStateChangeEvent<PanGestureHandlerEventPayload>
  finalize: GestureStateChangeEvent<PanGestureHandlerEventPayload>
}

const Context = createContext<{
  events: EventEmitter<GlobalGestureEvents>
  register: () => void
  unregister: () => void
}>({
  events: new EventEmitter<GlobalGestureEvents>(),
  register: () => {},
  unregister: () => {},
})

export function GlobalGestureEventsProvider({
  children,
}: {
  children: React.ReactNode
}) {
  const refCount = useRef(0)
  const events = useMemo(() => new EventEmitter<GlobalGestureEvents>(), [])
  const [enabled, setEnabled] = useState(false)
  const ctx = useMemo(
    () => ({
      events,
      register() {
        refCount.current += 1
        if (refCount.current === 1) {
          setEnabled(true)
        }
      },
      unregister() {
        refCount.current -= 1
        if (refCount.current === 0) {
          setEnabled(false)
        }
      },
    }),
    [events, setEnabled],
  )
  const gesture = Gesture.Pan()
    .runOnJS(true)
    .enabled(enabled)
    .simultaneousWithExternalGesture()
    .onBegin(e => {
      events.emit('begin', e)
    })
    .onUpdate(e => {
      events.emit('update', e)
    })
    .onEnd(e => {
      events.emit('end', e)
    })
    .onFinalize(e => {
      events.emit('finalize', e)
    })

  return (
    <Context.Provider value={ctx}>
      <GestureDetector gesture={gesture}>
        <View collapsable={false}>{children}</View>
      </GestureDetector>
    </Context.Provider>
  )
}

export function useGlobalGestureEvents() {
  return useContext(Context)
}
