import _extends from "@babel/runtime/helpers/esm/extends";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
var _excluded = ["prefixCls", "className", "style", "prefix", "split", "notFoundContent", "value", "defaultValue", "children", "options", "open", "allowClear", "silent", "validateSearch", "filterOption", "onChange", "onKeyDown", "onKeyUp", "onPressEnter", "onSearch", "onSelect", "onFocus", "onBlur", "transitionName", "placement", "direction", "getPopupContainer", "dropdownClassName", "rows", "visible", "onPopupScroll"],
  _excluded2 = ["suffix", "prefixCls", "defaultValue", "value", "allowClear", "onChange", "classNames", "className", "disabled", "onClear"];
import classNames from 'classnames';
import { BaseInput } from 'rc-input';
import TextArea from 'rc-textarea';
import toArray from "rc-util/es/Children/toArray";
import useMergedState from "rc-util/es/hooks/useMergedState";
import KeyCode from "rc-util/es/KeyCode";
import warning from "rc-util/es/warning";
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import useEffectState from "./hooks/useEffectState";
import KeywordTrigger from "./KeywordTrigger";
import MentionsContext from "./MentionsContext";
import Option from "./Option";
import { filterOption as defaultFilterOption, validateSearch as defaultValidateSearch, getBeforeSelectionText, getLastMeasureIndex, replaceWithMeasure, setInputSelection } from "./util";
var InternalMentions = /*#__PURE__*/forwardRef(function (props, ref) {
  var prefixCls = props.prefixCls,
    className = props.className,
    style = props.style,
    _props$prefix = props.prefix,
    prefix = _props$prefix === void 0 ? '@' : _props$prefix,
    _props$split = props.split,
    split = _props$split === void 0 ? ' ' : _props$split,
    _props$notFoundConten = props.notFoundContent,
    notFoundContent = _props$notFoundConten === void 0 ? 'Not Found' : _props$notFoundConten,
    value = props.value,
    defaultValue = props.defaultValue,
    children = props.children,
    options = props.options,
    open = props.open,
    allowClear = props.allowClear,
    silent = props.silent,
    _props$validateSearch = props.validateSearch,
    validateSearch = _props$validateSearch === void 0 ? defaultValidateSearch : _props$validateSearch,
    _props$filterOption = props.filterOption,
    filterOption = _props$filterOption === void 0 ? defaultFilterOption : _props$filterOption,
    onChange = props.onChange,
    onKeyDown = props.onKeyDown,
    onKeyUp = props.onKeyUp,
    onPressEnter = props.onPressEnter,
    onSearch = props.onSearch,
    onSelect = props.onSelect,
    onFocus = props.onFocus,
    onBlur = props.onBlur,
    transitionName = props.transitionName,
    placement = props.placement,
    direction = props.direction,
    getPopupContainer = props.getPopupContainer,
    dropdownClassName = props.dropdownClassName,
    _props$rows = props.rows,
    rows = _props$rows === void 0 ? 1 : _props$rows,
    visible = props.visible,
    onPopupScroll = props.onPopupScroll,
    restProps = _objectWithoutProperties(props, _excluded);
  var mergedPrefix = useMemo(function () {
    return Array.isArray(prefix) ? prefix : [prefix];
  }, [prefix]);

  // =============================== Refs ===============================
  var containerRef = useRef(null);
  var textareaRef = useRef(null);
  var measureRef = useRef(null);
  var getTextArea = function getTextArea() {
    var _textareaRef$current;
    return (_textareaRef$current = textareaRef.current) === null || _textareaRef$current === void 0 || (_textareaRef$current = _textareaRef$current.resizableTextArea) === null || _textareaRef$current === void 0 ? void 0 : _textareaRef$current.textArea;
  };
  React.useImperativeHandle(ref, function () {
    var _textareaRef$current4;
    return {
      focus: function focus() {
        var _textareaRef$current2;
        return (_textareaRef$current2 = textareaRef.current) === null || _textareaRef$current2 === void 0 ? void 0 : _textareaRef$current2.focus();
      },
      blur: function blur() {
        var _textareaRef$current3;
        return (_textareaRef$current3 = textareaRef.current) === null || _textareaRef$current3 === void 0 ? void 0 : _textareaRef$current3.blur();
      },
      textarea: (_textareaRef$current4 = textareaRef.current) === null || _textareaRef$current4 === void 0 || (_textareaRef$current4 = _textareaRef$current4.resizableTextArea) === null || _textareaRef$current4 === void 0 ? void 0 : _textareaRef$current4.textArea,
      nativeElement: containerRef.current
    };
  });

  // ============================== State ===============================
  var _useState = useState(false),
    _useState2 = _slicedToArray(_useState, 2),
    measuring = _useState2[0],
    setMeasuring = _useState2[1];
  var _useState3 = useState(''),
    _useState4 = _slicedToArray(_useState3, 2),
    measureText = _useState4[0],
    setMeasureText = _useState4[1];
  var _useState5 = useState(''),
    _useState6 = _slicedToArray(_useState5, 2),
    measurePrefix = _useState6[0],
    setMeasurePrefix = _useState6[1];
  var _useState7 = useState(0),
    _useState8 = _slicedToArray(_useState7, 2),
    measureLocation = _useState8[0],
    setMeasureLocation = _useState8[1];
  var _useState9 = useState(0),
    _useState10 = _slicedToArray(_useState9, 2),
    activeIndex = _useState10[0],
    setActiveIndex = _useState10[1];
  var _useState11 = useState(false),
    _useState12 = _slicedToArray(_useState11, 2),
    isFocus = _useState12[0],
    setIsFocus = _useState12[1];

  // ============================== Value ===============================
  var _useMergedState = useMergedState('', {
      defaultValue: defaultValue,
      value: value
    }),
    _useMergedState2 = _slicedToArray(_useMergedState, 2),
    mergedValue = _useMergedState2[0],
    setMergedValue = _useMergedState2[1];

  // =============================== Open ===============================
  useEffect(function () {
    // Sync measure div top with textarea for rc-trigger usage
    if (measuring && measureRef.current) {
      measureRef.current.scrollTop = getTextArea().scrollTop;
    }
  }, [measuring]);
  var _React$useMemo = React.useMemo(function () {
      if (open) {
        if (process.env.NODE_ENV !== 'production') {
          warning(false, '`open` of Mentions is only used for debug usage. Do not use in you production.');
        }
        for (var i = 0; i < mergedPrefix.length; i += 1) {
          var curPrefix = mergedPrefix[i];
          var index = mergedValue.lastIndexOf(curPrefix);
          if (index >= 0) {
            return [true, '', curPrefix, index];
          }
        }
      }
      return [measuring, measureText, measurePrefix, measureLocation];
    }, [open, measuring, mergedPrefix, mergedValue, measureText, measurePrefix, measureLocation]),
    _React$useMemo2 = _slicedToArray(_React$useMemo, 4),
    mergedMeasuring = _React$useMemo2[0],
    mergedMeasureText = _React$useMemo2[1],
    mergedMeasurePrefix = _React$useMemo2[2],
    mergedMeasureLocation = _React$useMemo2[3];

  // ============================== Option ==============================
  var getOptions = React.useCallback(function (targetMeasureText) {
    var list;
    if (options && options.length > 0) {
      list = options.map(function (item) {
        var _item$key;
        return _objectSpread(_objectSpread({}, item), {}, {
          key: (_item$key = item === null || item === void 0 ? void 0 : item.key) !== null && _item$key !== void 0 ? _item$key : item.value
        });
      });
    } else {
      list = toArray(children).map(function (_ref) {
        var optionProps = _ref.props,
          key = _ref.key;
        return _objectSpread(_objectSpread({}, optionProps), {}, {
          label: optionProps.children,
          key: key || optionProps.value
        });
      });
    }
    return list.filter(function (option) {
      /** Return all result if `filterOption` is false. */
      if (filterOption === false) {
        return true;
      }
      return filterOption(targetMeasureText, option);
    });
  }, [children, options, filterOption]);
  var mergedOptions = React.useMemo(function () {
    return getOptions(mergedMeasureText);
  }, [getOptions, mergedMeasureText]);

  // ============================= Measure ==============================
  // Mark that we will reset input selection to target position when user select option
  var onSelectionEffect = useEffectState();
  var startMeasure = function startMeasure(nextMeasureText, nextMeasurePrefix, nextMeasureLocation) {
    setMeasuring(true);
    setMeasureText(nextMeasureText);
    setMeasurePrefix(nextMeasurePrefix);
    setMeasureLocation(nextMeasureLocation);
    setActiveIndex(0);
  };
  var stopMeasure = function stopMeasure(callback) {
    setMeasuring(false);
    setMeasureLocation(0);
    setMeasureText('');
    onSelectionEffect(callback);
  };

  // ============================== Change ==============================
  var triggerChange = function triggerChange(nextValue) {
    setMergedValue(nextValue);
    onChange === null || onChange === void 0 || onChange(nextValue);
  };
  var onInternalChange = function onInternalChange(_ref2) {
    var nextValue = _ref2.target.value;
    triggerChange(nextValue);
  };
  var selectOption = function selectOption(option) {
    var _getTextArea;
    var _option$value = option.value,
      mentionValue = _option$value === void 0 ? '' : _option$value;
    var _replaceWithMeasure = replaceWithMeasure(mergedValue, {
        measureLocation: mergedMeasureLocation,
        targetText: mentionValue,
        prefix: mergedMeasurePrefix,
        selectionStart: (_getTextArea = getTextArea()) === null || _getTextArea === void 0 ? void 0 : _getTextArea.selectionStart,
        split: split
      }),
      text = _replaceWithMeasure.text,
      selectionLocation = _replaceWithMeasure.selectionLocation;
    triggerChange(text);
    stopMeasure(function () {
      // We need restore the selection position
      setInputSelection(getTextArea(), selectionLocation);
    });
    onSelect === null || onSelect === void 0 || onSelect(option, mergedMeasurePrefix);
  };

  // ============================= KeyEvent =============================
  // Check if hit the measure keyword
  var onInternalKeyDown = function onInternalKeyDown(event) {
    var which = event.which;
    onKeyDown === null || onKeyDown === void 0 || onKeyDown(event);

    // Skip if not measuring
    if (!mergedMeasuring) {
      return;
    }
    if (which === KeyCode.UP || which === KeyCode.DOWN) {
      // Control arrow function
      var optionLen = mergedOptions.length;
      var offset = which === KeyCode.UP ? -1 : 1;
      var newActiveIndex = (activeIndex + offset + optionLen) % optionLen;
      setActiveIndex(newActiveIndex);
      event.preventDefault();
    } else if (which === KeyCode.ESC) {
      stopMeasure();
    } else if (which === KeyCode.ENTER) {
      // Measure hit
      event.preventDefault();
      // loading skip
      if (silent) {
        return;
      }
      if (!mergedOptions.length) {
        stopMeasure();
        return;
      }
      var _option = mergedOptions[activeIndex];
      selectOption(_option);
    }
  };

  /**
   * When to start measure:
   * 1. When user press `prefix`
   * 2. When measureText !== prevMeasureText
   *  - If measure hit
   *  - If measuring
   *
   * When to stop measure:
   * 1. Selection is out of range
   * 2. Contains `space`
   * 3. ESC or select one
   */
  var onInternalKeyUp = function onInternalKeyUp(event) {
    var key = event.key,
      which = event.which;
    var target = event.target;
    var selectionStartText = getBeforeSelectionText(target);
    var _getLastMeasureIndex = getLastMeasureIndex(selectionStartText, mergedPrefix),
      measureIndex = _getLastMeasureIndex.location,
      nextMeasurePrefix = _getLastMeasureIndex.prefix;

    // If the client implements an onKeyUp handler, call it
    onKeyUp === null || onKeyUp === void 0 || onKeyUp(event);

    // Skip if match the white key list
    if ([KeyCode.ESC, KeyCode.UP, KeyCode.DOWN, KeyCode.ENTER].indexOf(which) !== -1) {
      return;
    }
    if (measureIndex !== -1) {
      var nextMeasureText = selectionStartText.slice(measureIndex + nextMeasurePrefix.length);
      var validateMeasure = validateSearch(nextMeasureText, split);
      var matchOption = !!getOptions(nextMeasureText).length;
      if (validateMeasure) {
        // adding AltGraph also fort azert keyboard
        if (key === nextMeasurePrefix || key === 'Shift' || which === KeyCode.ALT || key === 'AltGraph' || mergedMeasuring || nextMeasureText !== mergedMeasureText && matchOption) {
          startMeasure(nextMeasureText, nextMeasurePrefix, measureIndex);
        }
      } else if (mergedMeasuring) {
        // Stop if measureText is invalidate
        stopMeasure();
      }

      /**
       * We will trigger `onSearch` to developer since they may use for async update.
       * If met `space` means user finished searching.
       */
      if (onSearch && validateMeasure) {
        onSearch(nextMeasureText, nextMeasurePrefix);
      }
    } else if (mergedMeasuring) {
      stopMeasure();
    }
  };
  var onInternalPressEnter = function onInternalPressEnter(event) {
    if (!mergedMeasuring && onPressEnter) {
      onPressEnter(event);
    }
  };

  // ============================ Focus Blur ============================
  var focusRef = useRef();
  var onInternalFocus = function onInternalFocus(event) {
    window.clearTimeout(focusRef.current);
    if (!isFocus && event && onFocus) {
      onFocus(event);
    }
    setIsFocus(true);
  };
  var onInternalBlur = function onInternalBlur(event) {
    focusRef.current = window.setTimeout(function () {
      setIsFocus(false);
      stopMeasure();
      onBlur === null || onBlur === void 0 || onBlur(event);
    }, 0);
  };
  var onDropdownFocus = function onDropdownFocus() {
    onInternalFocus();
  };
  var onDropdownBlur = function onDropdownBlur() {
    onInternalBlur();
  };

  // ============================== Scroll ===============================
  var onInternalPopupScroll = function onInternalPopupScroll(event) {
    onPopupScroll === null || onPopupScroll === void 0 || onPopupScroll(event);
  };

  // ============================== Render ==============================

  return /*#__PURE__*/React.createElement("div", {
    className: classNames(prefixCls, className),
    style: style,
    ref: containerRef
  }, /*#__PURE__*/React.createElement(TextArea, _extends({
    ref: textareaRef,
    value: mergedValue
  }, restProps, {
    rows: rows,
    onChange: onInternalChange,
    onKeyDown: onInternalKeyDown,
    onKeyUp: onInternalKeyUp,
    onPressEnter: onInternalPressEnter,
    onFocus: onInternalFocus,
    onBlur: onInternalBlur
  })), mergedMeasuring && /*#__PURE__*/React.createElement("div", {
    ref: measureRef,
    className: "".concat(prefixCls, "-measure")
  }, mergedValue.slice(0, mergedMeasureLocation), /*#__PURE__*/React.createElement(MentionsContext.Provider, {
    value: {
      notFoundContent: notFoundContent,
      activeIndex: activeIndex,
      setActiveIndex: setActiveIndex,
      selectOption: selectOption,
      onFocus: onDropdownFocus,
      onBlur: onDropdownBlur,
      onScroll: onInternalPopupScroll
    }
  }, /*#__PURE__*/React.createElement(KeywordTrigger, {
    prefixCls: prefixCls,
    transitionName: transitionName,
    placement: placement,
    direction: direction,
    options: mergedOptions,
    visible: true,
    getPopupContainer: getPopupContainer,
    dropdownClassName: dropdownClassName
  }, /*#__PURE__*/React.createElement("span", null, mergedMeasurePrefix))), mergedValue.slice(mergedMeasureLocation + mergedMeasurePrefix.length)));
});
var Mentions = /*#__PURE__*/forwardRef(function (_ref3, ref) {
  var suffix = _ref3.suffix,
    _ref3$prefixCls = _ref3.prefixCls,
    prefixCls = _ref3$prefixCls === void 0 ? 'rc-mentions' : _ref3$prefixCls,
    defaultValue = _ref3.defaultValue,
    customValue = _ref3.value,
    allowClear = _ref3.allowClear,
    onChange = _ref3.onChange,
    classes = _ref3.classNames,
    className = _ref3.className,
    disabled = _ref3.disabled,
    onClear = _ref3.onClear,
    rest = _objectWithoutProperties(_ref3, _excluded2);
  // =============================== Ref ================================
  var holderRef = useRef(null);
  var mentionRef = useRef(null);
  useImperativeHandle(ref, function () {
    var _holderRef$current, _mentionRef$current;
    return _objectSpread(_objectSpread({}, mentionRef.current), {}, {
      nativeElement: ((_holderRef$current = holderRef.current) === null || _holderRef$current === void 0 ? void 0 : _holderRef$current.nativeElement) || ((_mentionRef$current = mentionRef.current) === null || _mentionRef$current === void 0 ? void 0 : _mentionRef$current.nativeElement)
    });
  });

  // ============================== Value ===============================
  var _useMergedState3 = useMergedState('', {
      defaultValue: defaultValue,
      value: customValue
    }),
    _useMergedState4 = _slicedToArray(_useMergedState3, 2),
    mergedValue = _useMergedState4[0],
    setMergedValue = _useMergedState4[1];

  // ============================== Change ==============================
  var triggerChange = function triggerChange(currentValue) {
    setMergedValue(currentValue);
    onChange === null || onChange === void 0 || onChange(currentValue);
  };

  // ============================== Reset ===============================
  var handleReset = function handleReset() {
    triggerChange('');
  };
  return /*#__PURE__*/React.createElement(BaseInput, {
    suffix: suffix,
    prefixCls: prefixCls,
    value: mergedValue,
    allowClear: allowClear,
    handleReset: handleReset,
    className: className,
    classNames: classes,
    disabled: disabled,
    ref: holderRef,
    onClear: onClear
  }, /*#__PURE__*/React.createElement(InternalMentions, _extends({
    className: classes === null || classes === void 0 ? void 0 : classes.mentions,
    prefixCls: prefixCls,
    ref: mentionRef,
    onChange: triggerChange,
    disabled: disabled
  }, rest)));
});
Mentions.Option = Option;
export default Mentions;