import {DM_MESSAGE_MAX} from '@crumbs/core/schemas'
import {useRef, useState} from 'react'
import {TextInput, useWindowDimensions, View} from 'react-native'
import {
  useFocusedInputHandler,
  useReanimatedKeyboardAnimation,
} from 'react-native-keyboard-controller'
import Animated, {
  measure,
  useAnimatedProps,
  useAnimatedRef,
  useAnimatedStyle,
  useSharedValue,
} from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context'

import {BottomBar} from '@/components/dm/bottom-bar'
import {Button} from '@/components/ui/button'
import {MAX_FONT_SCALE} from '@/components/ui/typography'
import {alertError} from '@/lib/dialogs'
import {atoms as a, tokens, useTheme} from '@/sprinkles'

const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)

const MIN_HEIGHT = 40
const AUTOCORRECT_FLUSH_MS = 20

// the inline chat composer: a plain text input + send. no mention
// autocomplete by design — dms are plain text.
//
// mirrors bluesky's MessageInput: controlled value with intrinsic autosize
// (flexBasis auto + minHeight, no static maxHeight) and a reanimated
// maxHeight / scrollEnabled that only kick in near the keyboard. one
// divergence forced by rn 0.85: on submit the native clear() runs
// alongside the state clear — growth needs the controlled commit, but
// shrinking needs the native setTextAndSelection path (a js-only clear
// never re-measures the input)
export const DmInputBar = ({
  onSend,
}: {
  onSend: (text: string) => Promise<void>
}) => {
  const t = useTheme()
  const insets = useSafeAreaInsets()
  const {height: windowHeight} = useWindowDimensions()
  const {height: keyboardHeight} = useReanimatedKeyboardAnimation()
  const maxHeight = useSharedValue<undefined | number>(undefined)
  const isInputScrollable = useSharedValue(false)
  const inputRef = useAnimatedRef<TextInput>()

  const [text, setText] = useState('')
  const isFlushingAutocorrect = useRef(false)

  useFocusedInputHandler(
    {
      onChangeText: () => {
        'worklet'
        const measurement = measure(inputRef)
        if (!measurement) return

        const max =
          windowHeight - -keyboardHeight.get() - insets.top - 150
        const availableSpace = max - measurement.height

        maxHeight.set(max)
        isInputScrollable.set(availableSpace < 30)
      },
    },
    [windowHeight, insets.top],
  )

  const animatedStyle = useAnimatedStyle(() => ({
    maxHeight: maxHeight.get(),
  }))

  const animatedProps = useAnimatedProps(() => ({
    scrollEnabled: isInputScrollable.get(),
  }))

  const submitDisabled = text.trim().length === 0

  const doSubmit = (message: string) => {
    const trimmed = message.trim()
    if (!trimmed) return
    setText('')
    inputRef.current?.clear()
    // defer send by a frame so the input resizes before the list updates
    requestAnimationFrame(() => {
      onSend(trimmed).catch(alertError)
    })
  }

  // ios prioritizes accepting a pending autocorrect suggestion over the
  // send tap, which would resurrect the text after our clear. mirror
  // bluesky's composer: arm a flag and race the corrected-text onChange
  // against a short timeout — send whichever text wins, so the clear
  // always removes the text actually in the input (a real native change,
  // which is what re-measures the height)
  const submit = () => {
    if (isFlushingAutocorrect.current) return
    if (!text.trim()) return
    isFlushingAutocorrect.current = true
    setTimeout(() => {
      if (!isFlushingAutocorrect.current) return
      isFlushingAutocorrect.current = false
      doSubmit(text)
    }, AUTOCORRECT_FLUSH_MS)
  }

  return (
    // resting safe-area padding lives in BottomBar; the sticky view's
    // `opened` offset slides it behind the keyboard so the input sits
    // flush when open
    <BottomBar
      gutter={tokens.space.sm}
      style={[a.flex_row, a.align_end, a.gap_sm]}
    >
      <View
        style={[
          a.flex_1,
          a.rounded_lg,
          {
            backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
            minHeight: MIN_HEIGHT,
          },
        ]}
      >
        <AnimatedTextInput
          ref={inputRef}
          maxFontSizeMultiplier={MAX_FONT_SCALE}
          value={text}
          onChange={(evt) => {
            const value = evt.nativeEvent.text
            if (isFlushingAutocorrect.current) {
              isFlushingAutocorrect.current = false
              setText(value)
              // defer a frame: a native clear() issued inside onChange
              // carries this event's stale eventCount and ios drops it,
              // skipping the re-measure
              requestAnimationFrame(() => doSubmit(value))
              return
            }
            setText(value)
          }}
          placeholder="Message"
          placeholderTextColor={t.atoms.text_contrast_medium.color}
          maxLength={DM_MESSAGE_MAX}
          multiline
          submitBehavior="newline"
          verticalAlign="middle"
          animatedProps={animatedProps}
          style={[
            {flexBasis: 'auto', flexShrink: 0, minHeight: MIN_HEIGHT},
            a.flex_grow,
            a.text_md,
            a.px_md,
            {paddingTop: 10, paddingBottom: 5},
            t.atoms.text,
            animatedStyle,
          ]}
        />
      </View>
      <Button
        label="Send"
        color="primary"
        size="small"
        disabled={submitDisabled}
        onPress={submit}
      />
    </BottomBar>
  )
}
