import {useCallback, useRef, useState} from 'react'
import {Pressable, View} from 'react-native'
import {useLingui} from '@lingui/react/macro'
import {flushSync} from 'react-dom'
import TextareaAutosize from 'react-textarea-autosize'
import {countGraphemes} from 'unicode-segmenter/grapheme'

import {MAX_DM_GRAPHEME_LENGTH} from '#/lib/constants'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {
  useMessageDraft,
  useSaveMessageDraft,
} from '#/state/messages/message-drafts'
import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons'
import {atoms as a, flatten, useTheme} from '#/alf'
import {Button} from '#/components/Button'
import * as EmojiPicker from '#/components/EmojiPicker'
import {useSharedInputStyles} from '#/components/forms/TextField'
import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji'
import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane'
import * as Toast from '#/components/Toast'
import {IS_WEB_SAFARI, IS_WEB_TOUCH_DEVICE} from '#/env'
import {useExtractEmbedFromFacets} from './MessageInputEmbed'

export function MessageInput({
  onSendMessage,
  hasEmbed,
  setEmbed,
  children,
}: {
  onSendMessage: (message: string) => void
  hasEmbed: boolean
  setEmbed: (embedUrl: string | undefined) => void
  children?: React.ReactNode
}) {
  const {isMobile} = useWebMediaQueries()
  const {t: l} = useLingui()
  const t = useTheme()
  const {getDraft, clearDraft} = useMessageDraft()
  const [message, setMessage] = useState(getDraft)

  const inputStyles = useSharedInputStyles()
  const isComposing = useRef(false)
  const [isFocused, setIsFocused] = useState(false)
  const [isHovered, setIsHovered] = useState(false)
  const [textAreaHeight, setTextAreaHeight] = useState(38)
  const textAreaRef = useRef<HTMLTextAreaElement>(null)

  const enableSquareButtons = useEnableSquareButtons()

  const onSubmit = useCallback(() => {
    if (!hasEmbed && message.trim() === '') {
      return
    }
    if (countGraphemes(message) > MAX_DM_GRAPHEME_LENGTH) {
      Toast.show(l`Message is too long`, {
        type: 'error',
      })
      return
    }
    clearDraft()
    onSendMessage(message)
    setMessage('')
    setEmbed(undefined)
  }, [message, onSendMessage, l, clearDraft, hasEmbed, setEmbed])

  const onKeyDown = useCallback(
    (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
      // Don't submit the form when the Japanese or any other IME is composing
      if (isComposing.current) return

      // see https://github.com/bluesky-social/social-app/issues/4178
      // see https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/
      // see https://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html
      //
      // On Safari, the final keydown event to dismiss the IME - which is the enter key - is also "Enter" below.
      // Obviously, this causes problems because the final dismissal should _not_ submit the text, but should just
      // stop the IME editing. This is the behavior of Chrome and Firefox, but not Safari.
      //
      // Keycode is deprecated, however the alternative seems to only be to compare the timestamp from the
      // onCompositionEnd event to the timestamp of the keydown event, which is not reliable. For example, this hack
      // uses that method: https://github.com/ProseMirror/prosemirror-view/pull/44. However, from my 500ms resulted in
      // far too long of a delay, and a subsequent enter press would often just end up doing nothing. A shorter time
      // frame was also not great, since it was too short to be reliable (i.e. an older system might have a larger
      // time gap between the two events firing.
      if (IS_WEB_SAFARI && e.key === 'Enter' && e.keyCode === 229) {
        return
      }

      if (e.key === 'Enter') {
        if (e.shiftKey) return
        e.preventDefault()
        onSubmit()
      }
    },
    [onSubmit],
  )

  const onChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
    setMessage(e.target.value)
  }, [])

  const onEmojiInserted = useCallback(
    (emoji: EmojiPicker.Emoji) => {
      if (!textAreaRef.current) {
        return
      }
      const position = textAreaRef.current.selectionStart ?? 0
      flushSync(() => {
        setMessage(
          message =>
            message.slice(0, position) + emoji.native + message.slice(position),
        )
      })
      textAreaRef.current.selectionStart = position + emoji.native.length
      textAreaRef.current.selectionEnd = position + emoji.native.length
    },
    [setMessage],
  )

  useSaveMessageDraft(message)
  useExtractEmbedFromFacets(message, setEmbed)

  return (
    <View style={a.p_sm}>
      {children}
      <View
        style={[
          a.flex_row,
          t.atoms.bg_contrast_25,
          {
            paddingRight: a.p_sm.padding - 2,
            paddingLeft: a.p_sm.padding - 2,
            borderWidth: 1,
            borderRadius: enableSquareButtons ? 11 : 23,
            borderColor: 'transparent',
            height: textAreaHeight + 23,
          },
          isHovered && inputStyles.chromeHover,
          isFocused && inputStyles.chromeFocus,
        ]}
        // @ts-expect-error web only
        onMouseEnter={() => setIsHovered(true)}
        onMouseLeave={() => setIsHovered(false)}>
        <EmojiPicker.Root
          onEmojiSelect={onEmojiInserted}
          nextFocusRef={textAreaRef}>
          <EmojiPicker.Trigger label={l`Open emoji picker`}>
            {({props, state}) => (
              <Button
                style={[
                  enableSquareButtons ? a.rounded_sm : a.rounded_full,
                  a.overflow_hidden,
                  a.align_center,
                  a.justify_center,
                  {
                    marginTop: 5,
                    height: 30,
                    width: 30,
                  },
                ]}
                label={props.accessibilityLabel}
                {...props}>
                <View
                  style={[
                    a.absolute,
                    a.inset_0,
                    a.align_center,
                    a.justify_center,
                    {
                      backgroundColor:
                        state.hovered || state.focused || state.pressed
                          ? t.atoms.bg.backgroundColor
                          : undefined,
                    },
                  ]}>
                  <EmojiSmile size="lg" />
                </View>
              </Button>
            )}
          </EmojiPicker.Trigger>
          <EmojiPicker.Picker />
        </EmojiPicker.Root>
        <TextareaAutosize
          ref={textAreaRef}
          style={flatten([
            a.flex_1,
            a.px_sm,
            a.border_0,
            t.atoms.text,
            {
              paddingTop: 10,
              backgroundColor: 'transparent',
              resize: 'none',
            },
          ])}
          maxRows={12}
          placeholder={l`Message`}
          defaultValue=""
          value={message}
          dirName="ltr"
          autoFocus={true}
          onFocus={() => setIsFocused(true)}
          onBlur={() => setIsFocused(false)}
          onCompositionStart={() => {
            isComposing.current = true
          }}
          onCompositionEnd={() => {
            isComposing.current = false
          }}
          onHeightChange={height => setTextAreaHeight(height)}
          onChange={onChange}
          // On mobile web phones, we want to keep the same behavior as the native app. Do not submit the message
          // in these cases.
          onKeyDown={IS_WEB_TOUCH_DEVICE && isMobile ? undefined : onKeyDown}
        />
        <Pressable
          accessibilityRole="button"
          accessibilityLabel={l`Send message`}
          accessibilityHint=""
          style={[
            enableSquareButtons ? a.rounded_sm : a.rounded_full,
            a.align_center,
            a.justify_center,
            {
              height: 30,
              width: 30,
              marginTop: 5,
              backgroundColor: t.palette.primary_500,
            },
          ]}
          onPress={onSubmit}>
          <PaperPlane fill={t.palette.white} style={[a.relative, {left: 1}]} />
        </Pressable>
      </View>
    </View>
  )
}
