import { jsx } from 'react/jsx-runtime';
import { forwardRef, useRef, useMemo, isValidElement, cloneElement } from 'react';
import { u as useMergeRefs } from './AiMark-voHPSerQ.js';
import { T as TextField } from './TextField-CUVsDfWo.js';
import { u as useOptionallyControlledState } from './useOptionallyControlledState-DbDuos5L.js';
import { c as childrenToString } from './childrenToString-Bz9MqbHb.js';
import { useTrackingId } from './useTrackingId.js';

const MASK_CHARS = ["a", "9", "*"];

function applyMask(value, mask, defaultMaskCharacter) {
  let maskedValue = "";
  let valueIndex = 0;
  for (let i = 0; i < mask.length; i++) {
    const maskChar = mask[i];
    if (MASK_CHARS.includes(maskChar)) {
      while (valueIndex < value.length && !isAcceptableChar(value[valueIndex], maskChar)) {
        valueIndex++;
      }
      if (valueIndex < value.length) {
        maskedValue += value[valueIndex];
        valueIndex++;
      } else {
        maskedValue += defaultMaskCharacter;
      }
    } else {
      maskedValue += maskChar;
    }
  }
  return maskedValue;
}
function isAcceptableChar(char, maskChar) {
  if (maskChar === "9") {
    return /\d/.test(char);
  }
  if (maskChar === "a") {
    return /[a-zA-Z]/.test(char);
  }
  if (maskChar === "*") {
    return /[a-zA-Z0-9]/.test(char);
  }
  return false;
}
function getNextOpenCharacterIndex(value, defaultMaskCharacter) {
  let idx = value.indexOf(defaultMaskCharacter);
  if (idx < 0) {
    idx = value.length;
  }
  return idx;
}
function getFirstNonFixedCharacterIndex(startingIndex, fixedCharacterIndices) {
  let idx = startingIndex;
  while (fixedCharacterIndices.includes(idx)) {
    idx++;
  }
  return idx;
}
function getLastFilledCharacterIndex(value, mask) {
  let idx = value.split("").findLastIndex((char, i) => isAcceptableChar(char, mask[i]));
  if (idx < 0) {
    idx = 0;
  }
  return idx;
}
function getFirstFilledCharacterIndex(value, mask) {
  let idx = value.split("").findIndex((char, i) => isAcceptableChar(char, mask[i]));
  if (idx < 0) {
    idx = 0;
  }
  return idx;
}

const InputMask = forwardRef(
  ({
    children,
    mask,
    defaultMaskCharacter = "_",
    value,
    defaultValue,
    onChange,
    inputProps,
    "data-tracking-id": dataTrackingId
  }, ref) => {
    const [v, setV] = useOptionallyControlledState({
      controlledValue: value !== void 0 ? applyMask(value, mask, defaultMaskCharacter) : void 0,
      defaultValue: applyMask(defaultValue || "", mask, defaultMaskCharacter)
      // Omitting the onChange handler here because we want to handle it ourselves (i.e. send an event instead of just the value)
    });
    const specialChange = useRef(null);
    const inputRef = useRef(null);
    const passInRef = useMergeRefs([inputRef, ref]);
    const currentOpenCharacterCount = useMemo(() => {
      return v.split("").filter((char) => char === defaultMaskCharacter).length;
    }, [v, defaultMaskCharacter]);
    const fixedCharacterIndices = useMemo(() => {
      const fixedIndices = [];
      mask.split("").forEach((char, i) => {
        if (!MASK_CHARS.includes(char)) {
          fixedIndices.push(i);
        }
      });
      return fixedIndices;
    }, [mask]);
    const moveCursorTo = (start, end = start) => {
      requestAnimationFrame(() => {
        inputRef.current?.setSelectionRange(start, end);
      });
    };
    const handleChange = (e) => {
      let newVal = applyMask(e.target.value, mask, defaultMaskCharacter);
      if (
        // A single character was backspaced
        specialChange.current?.reason === "Backspace" && specialChange.current.start === specialChange.current.end
      ) {
        if (fixedCharacterIndices.includes(specialChange.current.start - 1)) {
          const removeIndex = getLastFilledCharacterIndex(
            newVal.slice(0, specialChange.current.start),
            mask
          );
          newVal = applyMask(
            newVal.slice(0, removeIndex) + newVal.slice(removeIndex + 1),
            mask,
            defaultMaskCharacter
          );
          moveCursorTo(removeIndex, removeIndex);
        } else {
          const idx = Math.min(
            getLastFilledCharacterIndex(newVal, mask) + 1,
            specialChange.current.start - 1
          );
          moveCursorTo(idx, idx);
        }
      } else if (
        // A single character was deleted
        specialChange.current?.reason === "Delete" && specialChange.current.start === specialChange.current.end
      ) {
        if (fixedCharacterIndices.includes(specialChange.current.start)) {
          const removeIndex = getFirstFilledCharacterIndex(
            newVal.slice(specialChange.current.start + 1),
            mask
          );
          newVal = applyMask(
            newVal.slice(0, removeIndex) + newVal.slice(removeIndex + 1),
            mask,
            defaultMaskCharacter
          );
        }
        const firstFilledIdx = getFirstFilledCharacterIndex(newVal, mask);
        const targetIdx = Math.max(firstFilledIdx, specialChange.current.start);
        moveCursorTo(targetIdx, targetIdx);
      } else if (specialChange.current?.reason === "Edit" && specialChange.current.start === specialChange.current.end) {
        const firstNonFixedIdx = getFirstNonFixedCharacterIndex(
          specialChange.current.start + 1,
          fixedCharacterIndices
        );
        moveCursorTo(firstNonFixedIdx, firstNonFixedIdx);
      } else {
        const newIndex = getNextOpenCharacterIndex(
          newVal,
          defaultMaskCharacter
        );
        moveCursorTo(newIndex, newIndex);
      }
      setV(newVal);
      if (inputRef.current) {
        inputRef.current.value = newVal;
      }
      onChange?.({ ...e, target: { ...e.target, value: newVal } });
    };
    const handleFocus = (e) => {
      requestAnimationFrame(() => {
        if (e.target.selectionStart === null || e.target.selectionStart === void 0 || e.target.selectionStart >= v.length) {
          const newIndex = getNextOpenCharacterIndex(v, defaultMaskCharacter);
          moveCursorTo(newIndex, newIndex);
        }
      });
      inputProps?.onFocus?.(e);
    };
    const handleKeyDown = (e) => {
      const target = e.target;
      if (target.selectionStart === null || target.selectionEnd === null) {
        return;
      }
      if (e.key === "Backspace") {
        specialChange.current = {
          reason: "Backspace",
          start: target.selectionStart,
          end: target.selectionEnd
        };
      } else if (e.key === "Delete") {
        specialChange.current = {
          reason: "Delete",
          start: target.selectionStart,
          end: target.selectionEnd
        };
      } else if (currentOpenCharacterCount === 0) {
        specialChange.current = {
          reason: "Edit",
          start: target.selectionStart,
          end: target.selectionEnd
        };
      } else {
        specialChange.current = null;
      }
      inputProps?.onKeyDown?.(e);
    };
    const handleClick = (e) => {
      e.preventDefault();
      e.stopPropagation();
      inputProps?.onClick?.(e);
    };
    const handleBlur = (e) => {
      e.preventDefault();
      e.stopPropagation();
      inputProps?.onBlur?.(e);
    };
    const mergedInputProps = {
      ...inputProps,
      ...children && isValidElement(children) ? children.props : {},
      value: v,
      onChange: handleChange,
      onFocus: handleFocus,
      onKeyDown: handleKeyDown,
      onClick: handleClick,
      onBlur: handleBlur,
      ref: passInRef
    };
    const isInputEmpty = useMemo(() => {
      if (v.length === 0) {
        return true;
      }
      return v === applyMask("", mask, defaultMaskCharacter);
    }, [v, defaultMaskCharacter, mask]);
    const data = {
      label: "label" in mergedInputProps ? childrenToString(mergedInputProps.label) : null,
      labelProps: "labelProps" in mergedInputProps ? mergedInputProps.labelProps : null,
      prefix: childrenToString(mergedInputProps.prefix),
      hint: "hint" in mergedInputProps ? childrenToString(mergedInputProps.hint) : null,
      description: "description" in mergedInputProps ? childrenToString(mergedInputProps.description) : null,
      size: mergedInputProps.size,
      type: mergedInputProps.type
    };
    const trackingId = useTrackingId({
      name: "InputMask",
      data,
      hasOverride: !!dataTrackingId
    });
    return children && isValidElement(children) && children.type === "input" ? cloneElement(children, {
      ...mergedInputProps,
      "data-tracking-id": trackingId
    }) : /* @__PURE__ */ jsx(
      TextField,
      {
        "data-input-empty": isInputEmpty,
        "data-tracking-id": trackingId,
        ...mergedInputProps
      }
    );
  }
);
InputMask.displayName = "InputMask";

export { InputMask as I };
//# sourceMappingURL=InputMask-CX6XiOOm.js.map
