import React, { useCallback, useMemo, useState } from 'react'
import type { T } from '@deltachat/jsonrpc-client'
import { getLogger } from '@deltachat-desktop/shared/logger'
import { useRovingTabindex } from '../../contexts/RovingTabindex'
import { useMultiselect } from '../../hooks/useMultiselect'

const log = getLogger('messageFocusAndMultiselect')

type MessageMultiselectContextValue = ReturnType<
  typeof useMultiselect<T.Message['id']>
> & {
  resetSelection: () => void
}
export const MessageMultiselectContext =
  React.createContext<MessageMultiselectContextValue>({
    onClick: () => false,
    onFocus: () => {},
    onKeyDown: () => false,
    selectedItems: new Set(),
    resetSelection: () => {},
  })

export function useMessageFocusAndMultiselectContextValue(props: {
  messageIds: Array<T.Message['id']>
}) {
  const [selectedMessages_, setSelectedMessages] = useState(
    new Set<T.Message['id']>()
  )
  // As messages get deleted (possibly by other chat members
  // or from another device), remove them from selection.
  const selectedMessages = useMemo(() => {
    const newSelectedMessages = new Set(selectedMessages_)
    for (const id of selectedMessages_) {
      if (!props.messageIds.includes(id)) {
        newSelectedMessages.delete(id)
      }
    }
    return selectedMessages_.size === newSelectedMessages.size
      ? selectedMessages_
      : newSelectedMessages
  }, [props.messageIds, selectedMessages_])

  const resetSelection = useCallback(() => {
    setSelectedMessages(s => {
      if (s.size === 0) {
        return s
      }
      return new Set()
    })
  }, [])

  const multiselect = useMultiselect(
    props.messageIds,
    selectedMessages,
    setSelectedMessages,
    log,
    // Only enter "multi-select mode" on Shift + Click or Ctrl + Click.
    // Multi-selecting messages is not something that people do all the time.
    // It's annoying that a message keeps being displayed as selected
    // even though all you did is click a link inside of it.
    { onNormalClick: 'unselectAll' }
  )
  // Memoized because it is used as a context value.
  return useMemo(
    () => ({ ...multiselect, resetSelection }),
    [multiselect, resetSelection]
  )
}

export function useMessageFocusAndMultiselect(
  messageId: T.Message['id'],
  elementRef: Parameters<typeof useRovingTabindex>[0]
) {
  const rovingTabindex = useRovingTabindex(elementRef)
  const multiselect = React.useContext(MessageMultiselectContext)

  return {
    ...(rovingTabindex as Omit<
      typeof rovingTabindex,
      'onKeydown' | 'setAsActiveElement'
    >),
    ...multiselect,
    onClick: useCallback(
      (e: React.MouseEvent) => {
        const shouldPreventDefault = multiselect.onClick(e, messageId)
        if (shouldPreventDefault) {
          e.preventDefault()
        }
      },
      [messageId, multiselect]
    ),
    onFocus: useCallback(() => {
      rovingTabindex.setAsActiveElement()

      // Commented out for now. This disables the "Shift + ArrowDown
      // for contiguous selection" behavior, because it's a little buggy:
      // Pressing Shift + Tab would change selection
      // even if the user is simply trying to tab through the app.
      // Keyboard users can still utilize Shift + Space.
      // multiselect.onFocus(messageId)
    }, [rovingTabindex]),

    onKeyDown: useCallback(
      (e: React.KeyboardEvent) => {
        rovingTabindex.onKeydown(e)
        const shouldPreventDefault = multiselect.onKeyDown(e, messageId)
        if (shouldPreventDefault) {
          e.preventDefault()
        }
      },
      [messageId, multiselect, rovingTabindex]
    ),
  }
}
