import { useComposition } from "../hooks/use-composition.mjs";
import { isUndefined, isFunction, isNumber } from "../_utils/is.mjs";
import { Enter } from "../_utils/keycode.mjs";
import { ref, computed, watch, nextTick } from "vue";
function useInput(options) {
  const { modelValue, defaultValue, format, emits, emitUpdate, validate, valueOnInvalidChange, maxLength, minLength, calculateLength, inputOnOutlimit } = options;
  const formatFn = (v) => {
    return isFunction(format) ? format(v) : v;
  };
  const calculateStringLength = (v) => {
    return isFunction(calculateLength) ? calculateLength(v) : v == null ? void 0 : v.length;
  };
  const uncontroledValue = ref(defaultValue);
  const controledValue = modelValue;
  const computedValue = computed(() => {
    const cv = controledValue == null ? void 0 : controledValue.value;
    const ucv = uncontroledValue.value ?? "";
    return cv ?? ucv;
  });
  const displayValue = ref(formatFn(computedValue.value));
  const inputValueLength = computed(() => {
    return calculateStringLength(computedValue.value);
  });
  const validateMaxLength = (length) => {
    if (!isNumber(maxLength == null ? void 0 : maxLength.value)) {
      return true;
    }
    return length <= maxLength.value;
  };
  const validateMinLength = (length) => {
    if (!isNumber(minLength == null ? void 0 : minLength.value)) {
      return true;
    }
    return length >= minLength.value;
  };
  const validateLengthFn = (value) => {
    const len = calculateStringLength(value);
    return validateMaxLength(len) && validateMinLength(len);
  };
  const isOutLengthLimit = computed(() => {
    return !validateLengthFn(computedValue.value);
  });
  const mergedValidateFn = (v) => {
    const r = validateLengthFn(v);
    if (r && isFunction(validate)) {
      return validate(v);
    }
    return r;
  };
  const inputEl = ref();
  const composition = useComposition({ el: inputEl });
  const isFocus = ref(false);
  const isValid = ref(true);
  const validateValue = (value) => {
    isValid.value = value === "" ? true : mergedValidateFn(value);
    return isValid.value;
  };
  watch(
    () => [maxLength == null ? void 0 : maxLength.value, minLength == null ? void 0 : minLength.value],
    () => {
      validateValue(computedValue.value);
    }
  );
  let lastValidValue = validateValue(computedValue.value) ? computedValue.value : "";
  let lastValue = computedValue.value;
  watch(
    () => computedValue.value,
    (val) => {
      if (!isUndefined(val) && validateValue(val)) {
        lastValidValue = val;
      }
      if (isFocus.value) {
        displayValue.value = val;
      } else {
        displayValue.value = formatFn(val);
      }
    }
  );
  const updateValue = (value) => {
    uncontroledValue.value = value;
    if (value !== computedValue.value) {
      emitUpdate(value);
    }
  };
  const getValidValue = () => {
    let validVal = computedValue.value;
    if (!isValid.value) {
      if (isFunction(valueOnInvalidChange)) {
        validVal = valueOnInvalidChange(computedValue.value, lastValidValue);
        validateValue(validVal);
      } else if (lastValidValue !== "") {
        validVal = lastValidValue;
        isValid.value = true;
      }
    }
    return validVal;
  };
  const emitChange = (value) => {
    if (value !== lastValue) {
      nextTick(() => {
        lastValue = computedValue.value;
        emits("change", computedValue.value, lastValue);
        lastValue = computedValue.value;
      });
    }
  };
  const keepNativeDisplayValue = () => {
    if (inputEl.value && inputEl.value.value !== displayValue.value) {
      inputEl.value.value = displayValue.value;
    }
  };
  const isAllowedToInputOnOutLimit = (value) => {
    if (!isUndefined(maxLength == null ? void 0 : maxLength.value) && (inputOnOutlimit == null ? void 0 : inputOnOutlimit.value) === true) {
      return true;
    }
    const len = calculateStringLength(value);
    const isLower = validateMaxLength(len);
    if (isLower) {
      return true;
    }
    if (len < calculateStringLength(computedValue.value)) {
      return true;
    }
    return false;
  };
  const handleInput = (e) => {
    var _a;
    const value = (_a = e.target) == null ? void 0 : _a.value;
    if (composition.isComposing.value) {
      displayValue.value = value;
      return;
    }
    updateValue(value);
    emits("input", e, value);
    let newValue = value;
    emits("input", e, value);
    if (!isAllowedToInputOnOutLimit(value)) {
      newValue = value.substring(0, maxLength == null ? void 0 : maxLength.value);
      updateValue(newValue);
      nextTick(() => {
        keepNativeDisplayValue();
      });
    }
  };
  const handleFocus = (e) => {
    if (isFocus.value) {
      return;
    }
    isFocus.value = true;
    if (format) {
      displayValue.value = computedValue.value;
    }
    emits("focus", e);
  };
  const handleBlur = (e) => {
    isFocus.value = false;
    const validValue = getValidValue();
    updateValue(validValue);
    emitChange(validValue);
    displayValue.value = formatFn(computedValue.value);
    emits("blur", e);
  };
  const handlePressEnter = (e) => {
    const keyCode = e.key || e.code;
    if (!composition.isComposing.value && keyCode === Enter.key) {
      const validValue = getValidValue();
      updateValue(validValue);
      emitChange(validValue);
      emits("pressEnter", e);
    }
  };
  const clearValue = () => {
    displayValue.value = "";
    isValid.value = true;
    updateValue("");
    emitChange("");
    emits("clear");
  };
  const handleClear = (e) => {
    e.stopPropagation();
    e.preventDefault();
    clearValue();
  };
  return {
    realValue: computed(() => computedValue.value),
    displayValue: computed(() => displayValue.value),
    isValid,
    inputEl,
    clearValue,
    inputValueLength,
    isOutLengthLimit,
    handleInput,
    handleFocus,
    handleBlur,
    handlePressEnter,
    handleClear
  };
}
export {
  useInput
};
