import { type RefObject, useCallback, useEffect, useRef, useState } from 'react'
import {
  AccessibilityInfo,
  Keyboard,
  type NativeScrollEvent,
  type NativeSyntheticEvent,
  Platform,
} from 'react-native'
import {
  runOnJS,
  useAnimatedScrollHandler,
  useSharedValue,
} from 'react-native-reanimated'
import { scrollStore } from './scroll'
import { tabPressStore } from './tabPressStore'

export function useKeyboardVisible(): boolean {
  const [visible, setVisible] = useState(false)
  useEffect(() => {
    const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow'
    const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide'
    const showSub = Keyboard.addListener(showEvent, () => setVisible(true))
    const hideSub = Keyboard.addListener(hideEvent, () => setVisible(false))
    return () => {
      showSub.remove()
      hideSub.remove()
    }
  }, [])
  return visible
}

export interface A11yFlags {
  reduceTransparency: boolean
  reduceMotion: boolean
  boldText: boolean
}

export function useA11yFlags(): A11yFlags {
  const [flags, setFlags] = useState<A11yFlags>({
    reduceTransparency: false,
    reduceMotion: false,
    boldText: false,
  })
  useEffect(() => {
    let mounted = true
    const read = async () => {
      const [reduceTransparency, reduceMotion, boldText] = await Promise.all([
        AccessibilityInfo.isReduceTransparencyEnabled?.() ?? Promise.resolve(false),
        AccessibilityInfo.isReduceMotionEnabled?.() ?? Promise.resolve(false),
        AccessibilityInfo.isBoldTextEnabled?.() ?? Promise.resolve(false),
      ])
      if (!mounted) return
      setFlags({
        reduceTransparency: !!reduceTransparency,
        reduceMotion: !!reduceMotion,
        boldText: !!boldText,
      })
    }
    read()
    const subs = [
      AccessibilityInfo.addEventListener('reduceTransparencyChanged', (v) =>
        setFlags((prev) => ({ ...prev, reduceTransparency: !!v }))
      ),
      AccessibilityInfo.addEventListener('reduceMotionChanged', (v) =>
        setFlags((prev) => ({ ...prev, reduceMotion: !!v }))
      ),
      AccessibilityInfo.addEventListener('boldTextChanged', (v) =>
        setFlags((prev) => ({ ...prev, boldText: !!v }))
      ),
    ]
    return () => {
      mounted = false
      for (const s of subs) s?.remove?.()
    }
  }, [])
  return flags
}

// useTabPressScrollToTop — subscribe to the named tab's press event published
// on `tabPressStore`. The bar emits both on cross-tab navigation and on re-tap
// of the already-focused tab, so screens get a single unified "scroll to top
// + refetch" path. Routing by `routeName` (a stable string the screen passes
// in) sidesteps react-navigation's listener chain entirely — under
// expo-router the screen's `useNavigation()` is the inner stack, which never
// receives the outer tabs navigator's `tabPress` event.
type ScrollableRef = {
  scrollToOffset(opts: { animated?: boolean; offset: number }): void
}

export function useTabPressScrollToTop(
  routeName: string,
  listRef: RefObject<ScrollableRef | null>,
  onRefetch?: () => void
) {
  const onRefetchRef = useRef(onRefetch)
  onRefetchRef.current = onRefetch
  useEffect(
    () =>
      tabPressStore.subscribe(routeName, () => {
        listRef.current?.scrollToOffset({ animated: true, offset: 0 })
        onRefetchRef.current?.()
      }),
    [routeName, listRef]
  )
}

// useMenuScroll — returns an onScroll handler screens pass to their scroll
// view / list. Publishes minimize state to scrollStore when scroll delta
// exceeds the threshold. Opt-in per screen; only content-dense feeds that
// benefit from edge-to-edge immersion should enable it.
//
// The `enabled` flag lets the caller toggle per-tab without reconstructing
// the handler. When disabled, the handler is a no-op and the minimize flag
// resets to false.

const MINIMIZE_DOWN_THRESHOLD = 30
const MINIMIZE_UP_THRESHOLD = 8

export function useMenuScroll(enabled: boolean) {
  const lastY = useRef(0)

  useEffect(() => {
    if (!enabled) scrollStore.setMinimized(false)
    return () => {
      if (enabled) scrollStore.setMinimized(false)
    }
  }, [enabled])

  return useCallback(
    (e: NativeSyntheticEvent<NativeScrollEvent>) => {
      if (!enabled) return
      const y = e.nativeEvent.contentOffset.y
      const delta = y - lastY.current
      if (delta > MINIMIZE_DOWN_THRESHOLD && y > 0) {
        scrollStore.setMinimized(true)
        lastY.current = y
      } else if (delta < -MINIMIZE_UP_THRESHOLD || y <= 0) {
        scrollStore.setMinimized(false)
        lastY.current = y
      }
    },
    [enabled]
  )
}

// UI-thread variant of useMenuScroll. Pairs with an Animated.createAnimatedComponent-
// wrapped list so per-frame scroll events stay off the JS thread entirely. The
// threshold check runs as a worklet and only calls runOnJS(scrollStore.setMinimized)
// when the minimize flag *flips*, not on every frame.
export function useMenuAnimatedScroll(enabled: boolean) {
  const lastY = useSharedValue(0)
  const minimized = useSharedValue(false)

  useEffect(() => {
    if (!enabled) scrollStore.setMinimized(false)
    return () => {
      if (enabled) scrollStore.setMinimized(false)
    }
  }, [enabled])

  return useAnimatedScrollHandler(
    {
      onScroll: (event) => {
        'worklet'
        if (!enabled) return
        const y = event.contentOffset.y
        const delta = y - lastY.value
        if (delta > MINIMIZE_DOWN_THRESHOLD && y > 0) {
          lastY.value = y
          if (!minimized.value) {
            minimized.value = true
            runOnJS(scrollStore.setMinimized)(true)
          }
        } else if (delta < -MINIMIZE_UP_THRESHOLD || y <= 0) {
          lastY.value = y
          if (minimized.value) {
            minimized.value = false
            runOnJS(scrollStore.setMinimized)(false)
          }
        }
      },
    },
    [enabled]
  )
}
