import React, { useMemo, useState, useCallback, useRef, useEffect, memo } from 'react';
import isEqual from 'lodash-es/isEqual';
import classNames from 'classnames';
import { Editor, createEditor, Transforms, Range } from 'slate';
import { Slate, Editable, ReactEditor, withReact } from 'slate-react';
import { Button } from '../button';
import { PopperDropdownWrapper, DROPDOWN_PLACEMENT_TYPES } from '../dropdown';
import { TypeaheadOptionList, ItemComponents } from '../typeahead';
import { CharactersCounter } from './characters-counter';
import { withMaxLength, withMentions } from './hocs';
import { DEFAULT_VALUE, MENTION_PATTERN, WHITE_SPACE_PATTERN, MENTION_SEARCH_FIELD, MENTION_SEARCH_FIELDS } from './constants';
import { renderElement, insertMention } from './renderers';
import { getMentionsCount, resetNodes } from './utils';
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
function ChatMessageBodyBase({
  itemKey,
  textBoxAriaLabel,
  onChange,
  onSubmit,
  onMention,
  onBlur,
  readOnly,
  maxLength,
  getCharactersCounterString,
  mentions = [],
  actions,
  onActionBtnClick,
  editableContentClassName,
  mentionsPropmptText,
  autoFocus,
  value = DEFAULT_VALUE
}) {
  const anchorRef = useRef(null);
  const [target, setTarget] = useState();
  const [search, setSearch] = useState('');
  const [showPropmt, setShowPrompt] = useState(false);
  const [isMenuOpened, setIsMenuOpened] = useState(false);
  const hasMentionsSupport = !!onMention;
  const editor = useMemo(() => withMaxLength(maxLength)(withMentions(withReact(createEditor()))), []);
  const isFocused = ReactEditor.isFocused(editor);
  const onKeyDown = useCallback(event => {
    if (event.key === 'Enter' && target && mentions?.length) {
      event.preventDefault();
    }
    if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
      event.preventDefault();
      onSubmit({
        key: itemKey,
        value
      });
    }
  }, [mentions, target, value, itemKey, onSubmit]);
  const handleChange = useCallback(val => {
    onChange({
      key: itemKey,
      value: val
    });
    const {
      selection
    } = editor;
    if (selection && Range.isCollapsed(selection)) {
      const [start] = Range.edges(selection);
      const wordBefore = Editor.before(editor, start, {
        unit: 'word'
      });
      const before = wordBefore && Editor.before(editor, wordBefore);
      const beforeRange = before && Editor.range(editor, before, start);
      const beforeText = beforeRange && Editor.string(editor, beforeRange);
      const beforeMatch = beforeText?.match(MENTION_PATTERN);
      const after = Editor.after(editor, start);
      const afterRange = Editor.range(editor, start, after);
      const afterText = Editor.string(editor, afterRange);
      const afterMatch = afterText.match(WHITE_SPACE_PATTERN);
      const [node] = Editor.node(editor, selection);
      if (beforeMatch && afterMatch) {
        anchorRef.current = ReactEditor.toDOMNode(editor, node);
        setTarget(beforeRange);
        setSearch(beforeMatch[1]);
        setIsMenuOpened(true);
        setShowPrompt(false);
        return;
      }
      if (node.text.endsWith('@') && !search) {
        anchorRef.current = ReactEditor.toDOMNode(editor, node);
        setIsMenuOpened(true);
        setShowPrompt(true);
        return;
      }
    }
    setTarget(null);
    setSearch('');
    setIsMenuOpened(false);
    setShowPrompt(false);
    anchorRef.current = null;
  }, [onChange, itemKey]);
  const handleSelect = useCallback(({
    username,
    key
  }) => {
    Transforms.select(editor, target);
    insertMention(editor, username, key);
    setTarget(null);
  }, [editor, target]);
  const closeMenu = useCallback(() => setIsMenuOpened(false), []);
  const currLength = editor.children.length ? Editor.string(editor, []).length + getMentionsCount(editor.children) : 0;
  useEffect(() => {
    if (autoFocus && !readOnly) {
      ReactEditor.focus(editor);
      Transforms.select(editor, Editor.end(editor, []));
    }
  }, [readOnly]);
  useEffect(() => {
    onMention?.({
      value: search,
      key: itemKey
    });
  }, [search]);
  useEffect(() => {
    if (!isEqual(editor.children, value)) {
      resetNodes(editor, value);
    }
  }, [value, editor]);
  return /*#__PURE__*/_jsxs(_Fragment, {
    children: [/*#__PURE__*/_jsxs(Slate, {
      editor: editor,
      initialValue: value,
      onChange: handleChange,
      children: [/*#__PURE__*/_jsx("div", {
        "test-id": `chat-item-body-editable-content-${itemKey}`,
        className: classNames('chat-item-editable-content', editableContentClassName, {
          'read-only': readOnly
        }),
        children: /*#__PURE__*/_jsx(Editable, {
          className: "slate-content-editable",
          readOnly: readOnly,
          onKeyDown: onKeyDown
          // @ts-expect-error: late types written in the way that disallow custom elements used in their examples
          ,
          renderElement: renderElement,
          onBlur: onBlur,
          "aria-label": readOnly ? undefined : textBoxAriaLabel,
          onDOMBeforeInput: event => {
            // we have logic to prevent entering text longer than maxLength in hocs.ts
            // but there we prevent it only from updating slate inner state, html input still
            // adds entered text to the DOM input field causing buggy behavior: https://datarobot.atlassian.net/browse/TESTLIO-2963
            // preventing default from the onDOMBeforeInput won't add new character to the DOM input field.
            // we still want to leave logic in hocs.ts because it covers copy+paste case, as for some reason
            // under certain circumstances onDOMBeforeInput stops being triggering for copy+paste so having there is safer

            // we want to always allow deleting text
            if (!event.inputType.startsWith('delete')) {
              const existingMentionsCount = getMentionsCount(editor.children);
              if (!!event.data && Editor.string(editor, []).length + existingMentionsCount + (event?.data?.length ?? 0) > maxLength) {
                event.preventDefault();
              }
            }
          }
        })
      }), hasMentionsSupport && isFocused && /*#__PURE__*/_jsx(PopperDropdownWrapper, {
        closeDropdown: closeMenu,
        anchorRef: anchorRef.current,
        open: isMenuOpened,
        placement: DROPDOWN_PLACEMENT_TYPES.BOTTOM_START,
        contentClassName: "typeahead-dropdown-content",
        children: /*#__PURE__*/_jsxs(_Fragment, {
          children: [/*#__PURE__*/_jsx(TypeaheadOptionList, {
            optionComponent: /*#__PURE__*/_jsx(ItemComponents.DefaultItem, {
              nameKey: MENTION_SEARCH_FIELD
            }),
            searchFields: MENTION_SEARCH_FIELDS,
            searchValue: search,
            closeDropdown: closeMenu,
            handleSelect: handleSelect,
            optionId: "key",
            optionLabel: MENTION_SEARCH_FIELD,
            options: mentions
          }), showPropmt && /*#__PURE__*/_jsx("div", {
            className: "chat-mention-prompt",
            "test-id": "chat-mention-prompt",
            children: mentionsPropmptText
          })]
        })
      })]
    }), !readOnly && /*#__PURE__*/_jsxs(_Fragment, {
      children: [/*#__PURE__*/_jsx(CharactersCounter, {
        currLength: currLength,
        maxLength: maxLength,
        getCharactersCounterString: getCharactersCounterString
      }), !!actions?.length && /*#__PURE__*/_jsx("div", {
        className: "chat-item-actions",
        children: actions.map(({
          key,
          label,
          accentType,
          testId,
          isDisabled
        }) => /*#__PURE__*/_jsx(Button, {
          testId: testId,
          accentType: accentType,
          isDisabled: isDisabled,
          onClick: () => onActionBtnClick?.({
            itemKey,
            actionKey: key
          }),
          children: label
        }, key))
      })]
    })]
  });
}
ChatMessageBodyBase.displayName = 'ChatMessageBody';
export const ChatMessageBody = /*#__PURE__*/memo(ChatMessageBodyBase);