<template>
  <span
    :class="cls"
    @mousedown="handleMousedown"
    v-bind="wrapperAttrs"
    @mouseenter="scrollToRight"
    @mouseleave="scrollToLeft"
  >
    <span v-if="$slots.prefix" :class="`${prefixCls}-prefix`">
      <slot name="prefix"></slot>
    </span>
    <TransitionGroup
      tag="span"
      name="input-tag-zoom"
      ref="tag-inner"
      :class="[
        `${prefixCls}-inner`,
        excessTagsDisplayType
          ? `el-input-tag-inner-${excessTagsDisplayType}`
          : '',
      ]"
    >
      <div
        :class="[
          'el-input-tag-group',
          excessTagsDisplayType ? 'el-input-tag-' + excessTagsDisplayType : '',
        ]"
        key="input-tag-group"
        ref="tag-container"
      >
        <Tag
          v-for="(item, index) in tags"
          :key="`tag-${item.value}-${index}`"
          :class="`${prefixCls}-tag`"
          :closable="!mergedDisabled && !readonly && item.closable"
          visible
          :disable-transitions="true"
          v-bind="item.tagProps"
          @close="(ev) => handleRemove(item.value, index, ev)"
        >
          <!-- {{$slots.tag?.({ data: item.raw }) ??
            formatTag?.(item.raw) ??
            item.label}} -->
          {{ item.label }}
        </Tag>
      </div>

      <input
        v-bind="inputAttrs"
        ref="inputRef"
        key="input-tag-input"
        :class="`${prefixCls}-input`"
        :style="inputStyle"
        :placeholder="tags.length === 0 ? placeholder : undefined"
        :disabled="mergedDisabled"
        :readonly="readonly || disabledInput"
        @input="handleInput"
        @keydown="handleKeyDown"
        @focus="handleFocus"
        @blur="handleBlur"
        @compositionstart="handleComposition"
        @compositionupdate="handleComposition"
        @compositionend="handleComposition"
      />
    </TransitionGroup>
    <IconHover
      v-if="showClearBtn"
      :class="`${prefixCls}-clear-btn`"
      @onMousedown="(e) => e.stopPropagation()"
      @click.native="handleClear"
    >
      <i class="el-icon-close closebtn"></i>
    </IconHover>
    <span
      v-if="$slots.suffix || Boolean(feedback)"
      :class="`${prefixCls}-suffix`"
    >
      <slot name="suffix"></slot>
      <FeedbackIcon v-if="Boolean(feedback)" :type="feedback" />
    </span>
  </span>
</template>

<script>
import IconHover from "../_components/icon-hover.vue";
import FeedbackIcon from "../_components/feedback-icon.vue";
import formItemMixins from "../mixins/form-item";
import Tag from "../../tag";
import { omit, pick, getValueData, isUndefined, isNull } from "../_utils";

const DEFAULT_FIELD_NAMES = {
  value: "value",
  label: "label",
  closable: "closable",
  tagProps: "tagProps",
};

const INPUT_EVENTS = [
  // focus events
  "onFocus",
  "onFocusin",
  "onFocusout",
  "onBlur",
  // form events
  "onChange",
  "onBeforeinput",
  "onInput",
  "onReset",
  "onSubmit",
  "onInvalid",
  // keyboard events
  "onKeydown",
  "onKeypress",
  "onKeyup",
  // clipboard events
  "onCopy",
  "onCut",
  "onPaste",
  // composition events
  "onCompositionstart",
  "onCompositionupdate",
  "onCompositionend",
  // selection events
  "onSelect",
  // attrs
  "autocomplete",
  "autofocus",
  "maxlength",
  "minlength",
  "name",
  "pattern",
  "readonly",
  "required",
];

export default {
  name: "ElInputTag",
  componentName: "ElInputTag",
  inheritAttrs: false,
  components: { IconHover, FeedbackIcon, Tag },
  mixins: [formItemMixins],
  props: {
    /**
     * @zh 绑定值
     * @en Value
     * @vModel
     */
    modelValue: {
      type: Array,
      default: () => [],
    },
    inputValue: String,
    defaultInputValue: {
      type: String,
      default: "",
    },
    /**
     * @zh 占位符
     * @en Placeholder
     */
    placeholder: String,
    /**
     * @zh 是否禁用
     * @en Whether to disable
     */
    disabled: {
      type: Boolean,
      default: false,
    },
    /**
     * @zh 是否为错误状态
     * @en Whether it is an error state
     */
    error: {
      type: Boolean,
      default: false,
    },
    /**
     * @zh 是否为只读模式
     * @en Whether it is read-only mode
     */
    readonly: {
      type: Boolean,
      default: false,
    },
    /**
     * @zh 是否允许清空
     * @en Whether to allow clear
     */
    allowClear: {
      type: Boolean,
      default: false,
    },
    /**
     * @zh 是否仅创建唯一的值
     * @en Whether to create only unique values
     * @version 2.15.0
     */
    uniqueValue: {
      type: Boolean,
      default: false,
    },
    /**
     * @zh 输入框的大小
     * @en The size of the input
     * @values 'mini','small','medium','large'
     * @defaultValue 'medium'
     */
    size: {
      type: String,
      default: "large",
    },
    /**
     * @zh 最多展示的标签个数，`0` 表示不限制
     * @en The maximum number of tags displayed, `0` means unlimited
     */
    maxTagCount: {
      type: Number,
      default: 0,
    },
    /**
     * @zh 是否保留输入框的内容
     * @en Whether to keep the content of the input box
     * boolean | { create?: boolean; blur?: boolean }
     */
    retainInputValue: {
      type: [Boolean, Object],
      default: false,
    },
    /**
     * @zh 格式化标签内容
     * @en Format tag content
     */
    formatTag: {
      type: Function,
    },
    /**
     * @zh 自定义 `TagData` 中的字段
     * @en Customize fields in `TagData`
     * @version 2.22.0
     */
    fieldNames: {
      type: Object,
      default: () => {},
    },
    createType: {
      type: String,
      default: "enter",
    },
    baseCls: String,
    focused: Boolean,
    disabledInput: Boolean,
    uninjectFormItemContext: Boolean,
    /**
     * @zh 标签超出时的呈现方式，有两种：横向滚动显示 和 换行显示
     * 可选项：scroll/break
     */
    excessTagsDisplayType: {
      type: String,
      default: "scroll",
    },
  },
  data() {
    return {
      inputStyle: {
        // width: "12px",
      },
      _value: [],
      isComposition: false,
      _inputValue: "",
      _focused: false,
      compositionValue: "",
      isHover: false,
    };
  },
  computed: {
    cls() {
      return [
        this.prefixCls,
        `${this.prefixCls}-size-${this.mergedSize}`,
        {
          [`${this.prefixCls}-disabled`]: this.mergedDisabled,
          [`${this.prefixCls}-disabled-input`]: this.disabledInput,
          [`${this.prefixCls}-error`]: this.mergedError,
          [`${this.prefixCls}-focus`]: this.mergedFocused,
          [`${this.prefixCls}-readonly`]: this.readonly,
          [`${this.prefixCls}-has-tag`]: this.tags.length > 0,
          [`${this.prefixCls}-has-prefix`]: Boolean(this.$slots.prefix),
          [`${this.prefixCls}-has-suffix`]:
            Boolean(this.$slots.suffix) || this.showClearBtn || this.feedback,
          [`${this.prefixCls}-has-placeholder`]: !this.computedValue.length,
        },
      ];
    },
    computedInputValue() {
      if (isUndefined(this.inputValue) || isNull(this.inputValue)) {
        return this._inputValue;
      } else {
        return this.inputValue;
      }
    },
    _retainInputValue() {
      if (
        Object.prototype.toString.call(this.retainInputValue) ===
        "[object Object]"
      ) {
        return {
          create: false,
          blur: false,
          ...this.retainInputValue,
        };
      }
      return {
        create: this.retainInputValue,
        blur: this.retainInputValue,
      };
    },
    mergedFieldNames() {
      return {
        ...DEFAULT_FIELD_NAMES,
        ...this.fieldNames,
      };
    },
    computedValue() {
      if (isUndefined(this.modelValue) || isNull(this.modelValue)) {
        return this._value;
      } else {
        return this.modelValue;
      }
    },
    valueData() {
      return getValueData(this.computedValue, this.mergedFieldNames);
    },
    tags() {
      if (this.maxTagCount > 0) {
        const invisibleTags = this.valueData.length - this.maxTagCount;
        if (invisibleTags > 0) {
          const result = this.valueData.slice(0, this.maxTagCount);
          const raw = {
            value: "__arco__more",
            label: `+${invisibleTags}`,
            closable: false,
          };
          result.push({
            raw,
            ...raw,
          });
          return result;
        }
      }
      return this.valueData;
    },
    showClearBtn() {
      return (
        !this.mergedDisabled &&
        !this.readonly &&
        this.allowClear &&
        Boolean(this.computedValue.length) &&
        this.isHover
      );
    },
    wrapperAttrs() {
      return omit(this.$attrs, INPUT_EVENTS);
    },
    inputAttrs() {
      return pick(this.$attrs, INPUT_EVENTS);
    },
    prefixCls() {
      return this.baseCls || "el-input-tag";
    },
  },
  watch: {
    modelValue: {
      handler(value) {
        if (
          Object.prototype.toString.call(value) === "[object Null]" ||
          value === undefined
        ) {
          this._value = [];
        }
      },
      deep: true,
    },
    computedInputValue: {
      handler(value) {
        if (
          this.$refs.inputRef &&
          !this.isComposition &&
          value !== this.$refs.inputRef.value
        ) {
          this.$refs.inputRef.value = value;
        }
      },
      deep: true,
    },
  },
  created() {
    this._inputValue = this.defaultInputValue;
  },
  mounted() {
  },
  methods: {
    scrollToRight(type) {
      if(this.mergedDisabled){
        return
      }
      this.isHover = type !== 'out';
      this.setInputWidth();
      this.$nextTick(()=>{
        const element = this.$refs["tag-inner"].$el;
        element.scrollTo({ left: element.scrollWidth - element.clientWidth, behavior: 'smooth' });
      })
    },
    scrollToLeft() {
      this.isHover = this._focused;
      const element = this.$refs["tag-inner"].$el;
      element.scrollTo({ left: 0, behavior: "smooth" });
      this.setInputWidth();
    },
    handleMousedown(e) {
      // 如果点击的是close图标则不聚焦input
      if (e.target.className.includes("el-tag__close")) {
        return;
      }
      if (this.$refs.inputRef && e.target !== this.$refs.inputRef) {
        e.preventDefault();
        this.$refs.inputRef.focus();
      }
    },
    handleClear(e) {
      const element = this.$refs["tag-inner"].$el;
      element.scrollTo({ left: 0, behavior: "smooth" });
      const newValue = [];
      this.updateValue(newValue, e);
      this.$emit("clear", e);
    },
    handleRemove(value, index, e) {
      const newValue = this.computedValue.filter((_, i) => i !== index);
      this.updateValue(newValue, e);
      this.$emit("remove", value, e);
    },
    updateValue(value, ev) {
      this._value = [...value];
      this.$emit("update:modelValue", value);
      this.$emit("change", value, ev);
      // eventHandlers.value?.onChange?.(ev);
    },
    handleInput(ev) {
      const { value } = ev.target;

      if (!this.isComposition) {
        this.updateInputValue(value, ev);

        this.$nextTick(() => {
          if (
            this.$refs.inputRef &&
            this.computedInputValue !== this.$refs.inputRef.value
          ) {
            this.$refs.inputRef.value = this.computedInputValue;
          }
        });
      }
    },
    handleKeyDown(e) {
      const keyCode = e.key || e.code;
      if (
        !this.isComposition &&
        this.computedInputValue &&
        keyCode === "Enter"
      ) {
        this.handlePressEnter(e);
      }
      if (
        !this.isComposition &&
        this.tags.length > 0 &&
        !this.computedInputValue &&
        keyCode === "Backspace"
      ) {
        const lastIndex = this.valueData.length - 1;
        this.handleRemove(this.valueData[lastIndex].value, lastIndex, e);
      }
    },
    handleFocus(ev) {
      this._focused = true;
      this.$emit("focus", ev);
      // eventHandlers.value?.onFocus?.(ev);
    },
    handleBlur(ev) {
      this._focused = false;
      this.isHover = false;
      if (!this._retainInputValue.blur && this.computedInputValue) {
        if (this.createType === "enter") {
          this.updateInputValue("", ev);
        } else {
          this.handlePressEnter(ev);
        }
      }
      this.$emit("blur", ev);
      // eventHandlers.value?.onBlur?.(ev);
    },
    handleComposition(ev) {
      const { value } = ev.target;

      if (ev.type === "compositionend") {
        this.isComposition = false;
        this.compositionValue = "";
        this.updateInputValue(value, ev);

        this.$nextTick(() => {
          if (
            this.$refs.inputRef &&
            this.computedInputValue !== this.$refs.inputRef.value
          ) {
            this.$refs.inputRef.value = this.computedInputValue;
          }
        });
      } else {
        this.isComposition = true;
        this.compositionValue = this.computedInputValue + (ev.data || "");
      }
    },
    handlePressEnter(e) {
      if (this.computedInputValue) {
        e.preventDefault();
        if (
          this.uniqueValue &&
          this.computedValue.includes(this.computedInputValue)
        ) {
          this.$emit("pressEnter", this.computedInputValue, e);
          return;
        }
        const newValue = this.computedValue.concat(this.computedInputValue);
        this.updateValue(newValue, e);
        this.$emit("pressEnter", this.computedInputValue, e);
        if (!this._retainInputValue.create) {
          this.updateInputValue("", e);
        }
        if (this.excessTagsDisplayType === "scroll") {
          this.$nextTick(() => {
            this.scrollToRight(this.createType === "enter"?'':'out');
          });
        }
      }
    },
    updateInputValue(value, ev) {
      this._inputValue = value;
      this.$emit("update:inputValue", value);
      this.$emit("inputValueChange", value, ev);
    },
    setInputWidth() {
      if(this.isHover || this._focused){
        this.inputStyle.width = `32px`;
      }else{
        this.inputStyle.width = "0";
      }
    },
  },
};
</script>
