import type { DimSdkStore } from '#cli/core/store'
import type { ShortcutBindings } from '#cli/core/ui-settings'
import type { ComputedRef, Ref } from 'vue'
import type { MessageActionsMode } from '../../../ui/components/MessageActionsDialog'
import { t } from '#cli/i18n'
import {
  DEFAULT_SHORTCUTS,
  matchShortcut,
} from '#cli/core/ui-settings'
import { clamp } from '../../utils/math'
import { countWrappedLines } from './layout'

interface KeyEventLike {
  key?: string
  code?: string
  shiftKey?: boolean
  altKey?: boolean
  ctrlKey?: boolean
  metaKey?: boolean
  defaultPrevented?: boolean
  currentTarget?: { id?: string }
  preventDefault?: () => void
  stopPropagation?: () => void
  __bestAgentAskUserHandled?: boolean
}

type AskUserKeyAction =
  | { type: 'option'; index: number }
  | { type: 'nav'; direction: 'prev' | 'next' }
  | { type: 'optionFocus'; direction: 'up' | 'down' }
  | { type: 'focus'; target: 'options' | 'input' }
  | { type: 'toggleFocused' }
  | { type: 'activate' }
  | { type: 'submit' }
  | { type: 'cancel' }

export function useChatKeyboard(
  opts: Readonly<{
    nodeLike: boolean
    store: DimSdkStore
    events: Ref<{ focus?: (id: string) => void } | null>

    input: Ref<string>
    inputWrapWidth: ComputedRef<number>
    mentions?: Ref<readonly string[]>
    history: ComputedRef<readonly string[]>
    historyIndex: Ref<number>

    focusMode: Ref<'input' | 'messages'>
    requestInputFocus: () => void
    toolCallIds: ComputedRef<string[]>
    selectedToolCallId: ComputedRef<string | null>
    moveToolSelection: (delta: number) => void
    runSelected: () => Promise<void>

    anyDialogOpen: ComputedRef<boolean>
    showCommands: Ref<boolean>
    showHelp: Ref<boolean>
    showTheme: Ref<boolean>
    closeTheme: () => void
    showSessions: Ref<boolean>
    showPinned?: Ref<boolean>
    showMessageActions: Ref<boolean>
    messageActionsMode?: Ref<MessageActionsMode>
    showRedoConfirm: Ref<boolean>
    showConfig: Ref<boolean>
    showToolApprovals: Ref<boolean>
    showApproval: Ref<boolean>
    showAskUser: Ref<boolean>
    showPathPicker: Ref<boolean>
    showExitConfirm: Ref<boolean>

    openCommandPalette: () => void
    closeCommandPalette: () => void
    openHelp: () => void
    openTimeline?: () => void
    createNewSession?: () => void
    openConfig: () => void
    closeConfig: () => void
    closePathPicker: () => void
    toggleExpandToolCallsAndThinking: () => void
    cycleUiMode: (dir: 1 | -1) => void

    isLoading: ComputedRef<boolean>
    hasMessageSelection: ComputedRef<boolean>
    clearMessageSelection: () => void
    getSelectedMessageText: () => string
    copyTextToClipboard: (text: string) => Promise<boolean>
    showCopyToast: (text: string) => void
    onToolApprovalAction?: (
      action: 'approve' | 'alwaysApprove' | 'deny',
    ) => void
    approvalActionIndex?: Ref<number>
    ensurePendingApprovalVisible?: () => void
    onAskUserKey?: (action: AskUserKeyAction) => boolean
    isAskUserInputFocused?: () => boolean

    exitSessionToHome: () => Promise<void>
    onInterrupt?: () => void
    hasQueueUndo?: ComputedRef<boolean>
    onQueueUndo?: () => void
    shortcuts?: ComputedRef<ShortcutBindings>
  }>,
): Readonly<{
  onInputKeydown: (e: any) => void
  onSelectionKeydown: (e: any) => void
  onGlobalKeydown: (e: any) => void
  onGlobalKeydownCapture: (e: any) => void
}> {
  const getShortcuts = () => opts.shortcuts?.value ?? DEFAULT_SHORTCUTS

  function shouldOpenExitConfirmFromInput(value: string): boolean {
    // Consider mention-only input as "empty" for Esc -> Exit Session.
    // TInput stores focus-file mentions as tokens (`\uFFF9`), which are not visually "typed text".
    const normalized = String(value ?? '').replaceAll('\uFFF9', '')
    const trimmed = normalized.trim()
    // Treat a lone "/" (slash command trigger) as "empty" so Esc can fall through to the
    // session-exit affordance after dismissing the slash suggestions menu.
    return trimmed === '' || trimmed === '/' || trimmed === '@'
  }

  function isSlashLikeInput(value: string): boolean {
    const normalized = String(value ?? '').replaceAll('\uFFF9', '')
    return normalized.trimStart().startsWith('/')
  }

  function refocusStableTarget(e: KeyEventLike): void {
    const id = e?.currentTarget?.id
    if (typeof id === 'string' && id) opts.events.value?.focus?.(id)
  }

  function isToolNavArmed(): boolean {
    // Only handle tool navigation keys when the messages pane is focused.
    // If the chat input is focused, ArrowUp/ArrowDown should behave like normal
    // input navigation (history/cursor), even if a tool_call is currently selected.
    return opts.focusMode.value === 'messages'
  }

  function handleCopyFromSelection(
    e: KeyEventLike,
    opts2?: { allowAlt?: boolean },
  ): boolean {
    const allowAlt = Boolean(opts2?.allowAlt)
    const isClipboardShortcut = Boolean((e.metaKey || e.ctrlKey) && !e.altKey)
    const isAltCopyShortcut = Boolean(e.altKey && !e.metaKey && !e.ctrlKey)
    const isC = e.key === 'c' || e.key === 'C'
    if (!isC) return false

    const hasSelection = opts.hasMessageSelection.value
    if (!hasSelection) return false

    if (!isClipboardShortcut && !(allowAlt && isAltCopyShortcut)) return false

    const text = opts.getSelectedMessageText()
    if (!text) return false

    e.stopPropagation?.()
    e.preventDefault?.()
    void opts
      .copyTextToClipboard(text)
      .then((ok) =>
        opts.showCopyToast(
          ok
            ? t('chatPage.toast.copySuccess')
            : t('chatPage.toast.copyFailed'),
        ),
      )
      .catch(() => opts.showCopyToast(t('chatPage.toast.copyFailed')))
    return true
  }

  function handleToolApprovalKey(e: KeyEventLike): boolean {
    if (opts.anyDialogOpen.value) return false
    if (e.defaultPrevented) return false
    const active = opts.store.state.approval.active
    if (!active || active.status !== 'pending') return false
    if (e.ctrlKey || e.metaKey || e.altKey) return false
    const isApprovalInteractionKey =
      e.key === 'ArrowUp' ||
      e.key === 'ArrowDown' ||
      e.key === 'ArrowLeft' ||
      e.key === 'ArrowRight' ||
      e.key === 'Enter' ||
      e.key === '1' ||
      e.key === '2' ||
      e.key === '3'
    if (isApprovalInteractionKey) opts.ensurePendingApprovalVisible?.()
    const approvalActionsCount = 3
    const currentApprovalActionIndex = () =>
      clamp(
        Math.floor(Number(opts.approvalActionIndex?.value ?? 0)),
        0,
        approvalActionsCount - 1,
      )
    const setApprovalActionIndex = (next: number) => {
      if (!opts.approvalActionIndex) return
      opts.approvalActionIndex.value = clamp(
        Math.floor(next),
        0,
        approvalActionsCount - 1,
      )
    }
    const stepApprovalActionIndex = (delta: number) => {
      const next =
        (((currentApprovalActionIndex() + delta) % approvalActionsCount) +
          approvalActionsCount) %
        approvalActionsCount
      setApprovalActionIndex(next)
    }
    if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
      e.stopPropagation?.()
      e.preventDefault?.()
      stepApprovalActionIndex(-1)
      return true
    }
    if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
      e.stopPropagation?.()
      e.preventDefault?.()
      stepApprovalActionIndex(1)
      return true
    }
    if (e.key === 'Enter') {
      e.stopPropagation?.()
      e.preventDefault?.()
      const idx = currentApprovalActionIndex()
      const action =
        idx === 0 ? 'approve' : idx === 1 ? 'alwaysApprove' : 'deny'
      if (opts.onToolApprovalAction) {
        opts.onToolApprovalAction(action)
      } else if (action === 'approve') {
        void opts.store.approve()
      } else if (action === 'alwaysApprove') {
        opts.store.state.approval.mode = 'auto' as any
        void opts.store.approve()
      } else {
        void opts.store.deny()
      }
      return true
    }
    let action: 'approve' | 'alwaysApprove' | 'deny' | null = null
    if (e.key === '1') action = 'approve'
    else if (e.key === '2') action = 'alwaysApprove'
    else if (e.key === '3') action = 'deny'
    if (!action) return false
    e.stopPropagation?.()
    e.preventDefault?.()
    if (action === 'approve') setApprovalActionIndex(0)
    else if (action === 'alwaysApprove') setApprovalActionIndex(1)
    else setApprovalActionIndex(2)
    if (opts.onToolApprovalAction) {
      opts.onToolApprovalAction(action)
    } else if (action === 'approve') {
      void opts.store.approve()
    } else if (action === 'alwaysApprove') {
      opts.store.state.approval.mode = 'auto' as any
      void opts.store.approve()
    } else {
      void opts.store.deny()
    }
    return true
  }

  function isAskUserActive(): boolean {
    return opts.store.state.paused?.kind === 'ask_user'
  }

  function emitAskUserAction(
    e: KeyEventLike,
    action: AskUserKeyAction,
  ): boolean {
    if (!opts.onAskUserKey) return false
    if (opts.anyDialogOpen.value) return false
    if (!isAskUserActive()) return false
    if (e.__bestAgentAskUserHandled) return true
    const handled = Boolean(opts.onAskUserKey(action))
    if (!handled) return false
    e.__bestAgentAskUserHandled = true
    e.stopPropagation?.()
    e.preventDefault?.()
    return true
  }

  function handleAskUserInteractiveKey(
    e: KeyEventLike,
    scope: 'input' | 'messages',
  ): boolean {
    if (!opts.onAskUserKey) return false
    if (opts.anyDialogOpen.value) return false
    if (!isAskUserActive()) return false
    if (e.__bestAgentAskUserHandled) return true

    if ((e.ctrlKey || e.metaKey) && e.key === 'Enter')
      return emitAskUserAction(e, { type: 'submit' })
    if (e.key === 'Escape') return emitAskUserAction(e, { type: 'cancel' })

    if (e.ctrlKey || e.metaKey || e.altKey) return false

    const hasDraftInput = opts.input.value.trim().length > 0
    const askUserInputFocused =
      scope === 'input'
        ? opts.isAskUserInputFocused
          ? opts.isAskUserInputFocused()
          : true
        : false

    const isSpaceKey =
      e.key === ' '
      || e.key === 'Spacebar'
      || e.key === 'Space'
      || e.code === 'Space'

    if (e.key === 'Enter') return emitAskUserAction(e, { type: 'activate' })
    if (e.key === 'Tab')
      return emitAskUserAction(e, { type: 'focus', target: 'options' })

    if (e.key === 'ArrowUp')
      return emitAskUserAction(e, { type: 'optionFocus', direction: 'up' })
    if (e.key === 'ArrowDown')
      return emitAskUserAction(e, { type: 'optionFocus', direction: 'down' })
    if (
      scope === 'input' &&
      askUserInputFocused &&
      hasDraftInput &&
      (e.key === 'ArrowLeft' || e.key === 'ArrowRight')
    ) {
      return false
    }
    if (e.key === 'ArrowLeft')
      return emitAskUserAction(e, { type: 'nav', direction: 'prev' })
    if (e.key === 'ArrowRight')
      return emitAskUserAction(e, { type: 'nav', direction: 'next' })

    if (scope === 'input' && askUserInputFocused && hasDraftInput) return false

    if (scope === 'input' && askUserInputFocused && isSpaceKey) return false

    if (
      scope === 'messages' &&
      opts.isAskUserInputFocused?.() &&
      typeof e.key === 'string' &&
      e.key.length === 1
    ) {
      opts.input.value += e.key
      e.stopPropagation?.()
      e.preventDefault?.()
      return true
    }

    if (isSpaceKey)
      return emitAskUserAction(e, { type: 'toggleFocused' })

    return false
  }

  function handleAskUserNavShortcut(e: KeyEventLike): boolean {
    if (!opts.onAskUserKey) return false
    if (opts.anyDialogOpen.value) return false
    if (!isAskUserActive()) return false
    if (e.__bestAgentAskUserHandled) return true
    if (!(e.ctrlKey || e.metaKey)) return false
    const key = e.key
    if (key === 'ArrowLeft' || key === 'a' || key === 'A') {
      e.preventDefault?.()
      e.stopPropagation?.()
      const handled = Boolean(opts.onAskUserKey({ type: 'nav', direction: 'prev' }))
      if (handled) e.__bestAgentAskUserHandled = true
      return handled
    }
    if (key === 'ArrowRight' || key === 'e' || key === 'E') {
      e.preventDefault?.()
      e.stopPropagation?.()
      const handled = Boolean(opts.onAskUserKey({ type: 'nav', direction: 'next' }))
      if (handled) e.__bestAgentAskUserHandled = true
      return handled
    }
    return false
  }

  function onSelectionKeydown(e: any): void {
    if (opts.anyDialogOpen.value) return

    if (!opts.nodeLike)
      handleCopyFromSelection(e as KeyEventLike, { allowAlt: false })

    if (handleAskUserNavShortcut(e as KeyEventLike)) return
    if (handleToolApprovalKey(e as KeyEventLike)) return
    if (handleAskUserInteractiveKey(e as KeyEventLike, 'messages')) return

    if (e.key === 'ArrowUp') {
      e.preventDefault?.()
      opts.moveToolSelection(-1)
      refocusStableTarget(e)
      return
    }
    if (e.key === 'ArrowDown') {
      e.preventDefault?.()
      opts.moveToolSelection(1)
      refocusStableTarget(e)
      return
    }
    if (e.key === 'Enter') {
      if (!opts.selectedToolCallId.value) return
      e.preventDefault?.()
      void opts.runSelected().finally(() => refocusStableTarget(e))
    }
  }

  function onInputKeydown(e: any): void {
    // Exit confirm should capture keys even if focus remains in the input.
    if (opts.showExitConfirm.value) {
      if (e.key === 'Escape') {
        e.preventDefault?.()
        e.stopPropagation?.()
        opts.showExitConfirm.value = false
        return
      }
      if (e.key === 'Enter') {
        e.preventDefault?.()
        e.stopPropagation?.()
        opts.showExitConfirm.value = false
        void opts.exitSessionToHome()
        return
      }
      if (
        typeof e?.key === 'string' &&
        e.key.length === 1 &&
        !e.ctrlKey &&
        !e.metaKey &&
        !e.altKey
      ) {
        e.preventDefault?.()
        e.stopPropagation?.()
        return
      }
    }

    // In CLI mode, prioritize interrupting an in-flight/streaming run over input-level Escape
    // behavior (e.g. TInput blurring). This keeps Esc responsive during mid-render streams.
    if (opts.nodeLike && e.key === 'Escape' && opts.isLoading.value) {
      e.preventDefault?.()
      e.stopPropagation?.()
      if (opts.onInterrupt) opts.onInterrupt()
      else opts.store.interrupt()
      return
    }

    if (handleToolApprovalKey(e as KeyEventLike)) return
    if (handleAskUserNavShortcut(e as KeyEventLike)) return
    if (handleAskUserInteractiveKey(e as KeyEventLike, 'input')) return

    const shouldExitFromEsc =
      e.key === 'Escape' && shouldOpenExitConfirmFromInput(opts.input.value)
    const allowEscFromPrevented =
      shouldExitFromEsc && !isSlashLikeInput(opts.input.value)
    if (e.defaultPrevented && !allowEscFromPrevented) return

    if (!opts.anyDialogOpen.value && e.key === 'F1') {
      e.preventDefault?.()
      opts.openHelp()
      return
    }

    if (matchShortcut(e as KeyEventLike, getShortcuts().commands)) {
      e.preventDefault?.()
      opts.openCommandPalette()
      return
    }
    if (matchShortcut(e as KeyEventLike, getShortcuts().timeline)) {
      e.preventDefault?.()
      opts.openTimeline?.()
      return
    }
    if (matchShortcut(e as KeyEventLike, getShortcuts().newSession)) {
      e.preventDefault?.()
      opts.createNewSession?.()
      return
    }
    if (
      (e.ctrlKey || e.metaKey) &&
      !e.altKey &&
      (e.key === 'z' || e.key === 'Z')
    ) {
      if (opts.hasQueueUndo?.value) {
        e.preventDefault?.()
        e.stopPropagation?.()
        opts.onQueueUndo?.()
        return
      }
    }
    if (e.key === 'Tab' && !e.metaKey && !e.ctrlKey && !e.altKey) {
      e.preventDefault?.()
      opts.cycleUiMode(e.shiftKey ? -1 : 1)
      return
    }
    if ((e.ctrlKey || e.metaKey) && (e.key === 'k' || e.key === 'K')) {
      e.preventDefault?.()
      opts.openConfig()
      return
    }
    if (
      (e.ctrlKey || e.metaKey) &&
      !e.altKey &&
      (e.key === 'o' || e.key === 'O')
    ) {
      e.preventDefault?.()
      opts.toggleExpandToolCallsAndThinking()
      return
    }

    // Convenience: allow running the currently selected tool_call from the input
    // when the input is empty. (Avoids needing to move focus to messages.)
    if (!opts.anyDialogOpen.value && e.key === 'Enter') {
      if (!opts.selectedToolCallId.value) return
      if (opts.input.value.trim()) return
      e.preventDefault?.()
      void opts.runSelected()
      return
    }

    if (e.defaultPrevented && !allowEscFromPrevented) return

    // Only switch history when input visually fits in one line.
    const visualLineCount = countWrappedLines(
      opts.input.value,
      opts.inputWrapWidth.value,
      opts.mentions?.value ?? [],
    )

    if (e.key === 'ArrowUp') {
      if (visualLineCount > 1) return
      if (opts.history.value.length === 0) return
      e.preventDefault?.()
      opts.historyIndex.value = clamp(
        opts.historyIndex.value - 1,
        0,
        opts.history.value.length - 1,
      )
      opts.input.value = opts.history.value[opts.historyIndex.value] ?? ''
      return
    }
    if (e.key === 'ArrowDown') {
      if (visualLineCount > 1) return
      if (opts.history.value.length === 0) return
      e.preventDefault?.()
      if (opts.historyIndex.value >= opts.history.value.length - 1) {
        opts.historyIndex.value = opts.history.value.length
        opts.input.value = ''
        return
      }
      opts.historyIndex.value = clamp(
        opts.historyIndex.value + 1,
        0,
        opts.history.value.length - 1,
      )
      opts.input.value = opts.history.value[opts.historyIndex.value] ?? ''
    }

    if (e.key === 'Escape' && shouldExitFromEsc) {
      e.preventDefault?.()
      e.stopPropagation?.()
      if (opts.isLoading.value) {
        opts.store.interrupt()
        return
      }
      opts.showExitConfirm.value = true
    }
  }

  function handleEscapeOrInterrupt(): void {
    if (opts.showRedoConfirm.value) {
      opts.showRedoConfirm.value = false
    } else if (opts.showCommands.value) {
      opts.closeCommandPalette()
    } else if (opts.showHelp.value) {
      opts.showHelp.value = false
    } else if (opts.showPinned?.value) {
      opts.showPinned.value = false
    } else if (opts.showTheme.value) {
      opts.closeTheme()
    } else if (opts.showSessions.value) {
      opts.showSessions.value = false
    } else if (opts.showMessageActions.value) {
      opts.showMessageActions.value = false
      if (opts.messageActionsMode) opts.messageActionsMode.value = 'full'
    } else if (opts.showConfig.value) {
      opts.closeConfig()
    } else if (opts.showToolApprovals.value) {
      opts.showToolApprovals.value = false
    } else if (opts.store.state.paused?.kind === 'ask_user') {
      opts.store.cancelAskUser()
    } else if (opts.showPathPicker.value) {
      opts.closePathPicker()
    } else if (opts.showExitConfirm.value) {
      opts.showExitConfirm.value = false
    } else if (opts.isLoading.value) {
      opts.store.interrupt()
    } else if (opts.hasMessageSelection.value) {
      opts.clearMessageSelection()
    } else {
      opts.showExitConfirm.value = true
    }
  }

  function onGlobalKeydown(e: any): void {
    const globalShouldExitFromEsc =
      e.key === 'Escape' && shouldOpenExitConfirmFromInput(opts.input.value)

    // Copy from message selection:
    // - Browser: Cmd/Ctrl+C (works like normal copy)
    // - CLI: copy happens automatically on mouse selection release
    if (
      !opts.nodeLike &&
      handleCopyFromSelection(e as KeyEventLike, { allowAlt: false })
    )
      return

    if (opts.showExitConfirm.value && e.key === 'Enter') {
      e.stopPropagation?.()
      e.preventDefault?.()
      opts.showExitConfirm.value = false
      void opts.exitSessionToHome()
      return
    }

    // Let the focused control handle the key first (e.g. TInput copy with a selection).
    if (e.defaultPrevented) {
      if (
        globalShouldExitFromEsc &&
        !isSlashLikeInput(opts.input.value) &&
        !opts.anyDialogOpen.value &&
        !opts.isLoading.value
      ) {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.showExitConfirm.value = true
      }
      return
    }

    if (!opts.anyDialogOpen.value) {
      if (e.key === 'F1') {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.openHelp()
        return
      }
      if (
        (e.ctrlKey || e.metaKey) &&
        !e.altKey &&
        (e.key === 'k' || e.key === 'K')
      ) {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.openConfig()
        return
      }
      if (
        (e.ctrlKey || e.metaKey) &&
        !e.altKey &&
        (e.key === 'o' || e.key === 'O')
      ) {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.toggleExpandToolCallsAndThinking()
        return
      }
      if (matchShortcut(e as KeyEventLike, getShortcuts().commands)) {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.openCommandPalette()
        return
      }
      if (matchShortcut(e as KeyEventLike, getShortcuts().timeline)) {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.openTimeline?.()
        return
      }
      if (matchShortcut(e as KeyEventLike, getShortcuts().newSession)) {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.createNewSession?.()
        return
      }
      if (e.key === 'Tab' && !e.metaKey && !e.ctrlKey && !e.altKey) {
        e.stopPropagation?.()
        e.preventDefault?.()
        if (opts.focusMode.value !== 'input') {
          opts.focusMode.value = 'input'
          opts.requestInputFocus()
          return
        }
        opts.cycleUiMode(e.shiftKey ? -1 : 1)
        return
      }
    }

    if (
      (e.ctrlKey || e.metaKey) &&
      !e.altKey &&
      (e.key === 'r' || e.key === 'R')
    ) {
      if (opts.store.hasRevertedMessages.value) {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.showRedoConfirm.value = true
        return
      }
    }

    if (e.key !== 'Escape') return
    e.stopPropagation?.()
    e.preventDefault?.()

    handleEscapeOrInterrupt()
  }

  function onGlobalKeydownCapture(e: any): void {
    if (opts.anyDialogOpen.value) return

    if (handleAskUserNavShortcut(e as KeyEventLike)) return
    if (
      handleAskUserInteractiveKey(
        e as KeyEventLike,
        opts.isAskUserInputFocused?.() ? 'input' : 'messages',
      )
    ) {
      return
    }

    if (e.key === 'Tab' && !e.metaKey && !e.ctrlKey && !e.altKey) {
      if (opts.focusMode.value !== 'input') {
        e.stopPropagation?.()
        e.preventDefault?.()
        opts.focusMode.value = 'input'
        opts.requestInputFocus()
      }
      return
    }

    if (handleToolApprovalKey(e as KeyEventLike)) return

    if (!isToolNavArmed()) return

    if (e.key === 'ArrowUp') {
      e.stopPropagation?.()
      e.preventDefault?.()
      opts.moveToolSelection(-1)
      return
    }
    if (e.key === 'ArrowDown') {
      e.stopPropagation?.()
      e.preventDefault?.()
      opts.moveToolSelection(1)
      return
    }
    if (
      e.key === 'Enter' &&
      opts.selectedToolCallId.value &&
      !opts.input.value.trim()
    ) {
      e.stopPropagation?.()
      e.preventDefault?.()
      void opts.runSelected()
    }
  }

  return {
    onInputKeydown,
    onSelectionKeydown,
    onGlobalKeydown,
    onGlobalKeydownCapture,
  }
}
