import { jsx, jsxs, Fragment } from "react/jsx-runtime";
import * as React from "react";
import { useBindingState } from "../../form/hooks/useBindingState.js";
import { findLastIndex } from "../../utils/arrays.js";
import { concat } from "../../utils/classNames.js";
import "../../utils/globals.js";
import "zod";
import { useForm } from "../../form/hooks/useForm.js";
import { useDidUpdateEffect } from "../../hooks/useDidUpdateEffect.js";
import { useArmstrongConfig } from "../config/config.context.js";
import { Input } from "../input/input.component.js";
import { Label } from "../label/label.component.js";
import { StatusWrapper } from "../statusWrapper/statusWrapper.component.js";
import { ValidationErrors } from "../validationErrors/validationErrors.component.js";
import { getLengthFromPart } from "./codeInput.utils.js";
import "./codeInput.theme.css.js";
const CodeInputPart = React.forwardRef(
  ({ bind, part, ...inputProps }, ref) => {
    const length = React.useMemo(() => getLengthFromPart(part), [part]);
    if (typeof part === "string") {
      return jsx("p", { className: "arm-code-input-part-text", children: part });
    }
    if (typeof part === "number") {
      return jsx(Input, { ref, bind, ...inputProps, className: "arm-code-input-part-input", style: { "--arm-code-input-length": length }, "data-length": length, onClick: (event) => event.currentTarget.select(), maxLength: part });
    }
    const { className, ...textInputProps } = part;
    return jsx(Input, { ref, bind, className: concat("arm-code-input-part-input", className), style: {
      ...textInputProps.style || {},
      "--arm-code-input-length": length
    }, "data-length": length, onClick: (event) => event.currentTarget.select(), ...inputProps, ...textInputProps, maxLength: part.length, displaySize: part.displaySize });
  }
  // type assertion to ensure generic works with RefForwarded component
  // DO NOT CHANGE TYPE WITHOUT CHANGING THIS, FIND TYPE BY INSPECTING React.forwardRef
);
CodeInputPart.displayName = "CodeInputPart";
const CodeInput = React.forwardRef(
  ({ className, parts, bind, onChange, validationMode, validationErrorMessages, errorIcon, error, value, pending, statusPosition, leftOverlay, rightOverlay, scrollValidationErrorsIntoView, displaySize, label, required, requiredIndicator, hideIconOnStatus, statusClassName, validationErrorsClassName, labelClassName, labelId, disableOnPending, disabled, autoValidate, ...nativeProps }, ref) => {
    var _a;
    const inputRefs = React.useRef([]);
    const [boundValue, setBoundValue, bindConfig] = useBindingState(bind, {
      onChange,
      validationErrorMessages,
      validationMode,
      value,
      autoValidate
    });
    const globals = useArmstrongConfig({
      validationMode: bindConfig.validationMode,
      inputStatusPosition: statusPosition,
      inputDisplaySize: displaySize,
      requiredIndicator,
      hideInputErrorIconOnStatus: hideIconOnStatus,
      disableControlOnPending: disableOnPending,
      scrollValidationErrorsIntoView,
      validationErrorIcon: errorIcon,
      autoValidate: bindConfig.autoValidate
    });
    const goNextPart = React.useCallback((partIndex) => {
      var _a2;
      const nextIndex = parts.slice(partIndex + 1).findIndex((part) => typeof part !== "string") + partIndex + 1;
      if (nextIndex !== -1) {
        (_a2 = inputRefs.current[nextIndex]) == null ? void 0 : _a2.focus();
      }
    }, [parts]);
    const goPreviousPart = React.useCallback((partIndex) => {
      var _a2;
      const previousIndex = findLastIndex(parts.slice(0, partIndex), (part) => typeof part !== "string");
      if (previousIndex !== -1) {
        (_a2 = inputRefs.current[previousIndex]) == null ? void 0 : _a2.focus();
      }
    }, [parts]);
    const getValueForPart = React.useCallback((partIndex, incomingValue) => {
      const sliceStart = parts.slice(0, partIndex).reduce((output, part) => output + getLengthFromPart(part), 0);
      const sliceEnd = sliceStart + getLengthFromPart(parts[partIndex]);
      return (incomingValue == null ? void 0 : incomingValue.slice(sliceStart, sliceEnd)) || "";
    }, [parts]);
    const onPartValueChange = React.useCallback((event, partIndex) => {
      const currentPartLength = getLengthFromPart(parts[partIndex]);
      const currentPartValue = event.currentTarget.value || "";
      if (currentPartValue.length >= currentPartLength) {
        goNextPart(partIndex);
      }
    }, [parts, goNextPart]);
    const onPaste = React.useCallback((event) => {
      event.preventDefault();
      const pasteValue = event.clipboardData.getData("text/plain");
      const target = event.target;
      const partIndex = inputRefs.current.indexOf(target);
      const selectionStart = Math.min(target.selectionStart ?? 0, target.selectionEnd ?? 0);
      const selectionEnd = Math.max(target.selectionStart ?? 0, target.selectionEnd ?? 0);
      const insertionStart = new Array(partIndex).fill(null).reduce((memo, _, i) => {
        const part = parts[i];
        return memo + getLengthFromPart(part);
      }, 0) + selectionStart;
      const insertionEnd = insertionStart + (selectionEnd - selectionStart);
      const startSlice = (boundValue == null ? void 0 : boundValue.slice(0, insertionStart)) ?? "";
      const endSlice = (boundValue == null ? void 0 : boundValue.slice(insertionEnd)) ?? "";
      const newValue = startSlice + pasteValue + endSlice;
      setBoundValue(newValue);
    }, [setBoundValue, boundValue, parts]);
    const boundValueArray = React.useMemo(() => {
      return parts.map((_, partIndex) => getValueForPart(partIndex, boundValue));
    }, [getValueForPart, parts, boundValue]);
    const onKeyDown = React.useCallback((event, partIndex, part) => {
      var _a2;
      switch (event.key) {
        case "Backspace": {
          if (((_a2 = event.currentTarget.value) == null ? void 0 : _a2.length) <= 0 && partIndex > 0) {
            goPreviousPart(partIndex);
          }
          break;
        }
        case "ArrowLeft": {
          if (event.currentTarget.selectionStart === 0 && partIndex > 0) {
            goPreviousPart(partIndex);
          }
          break;
        }
        case "ArrowRight": {
          if (event.currentTarget.selectionEnd === getLengthFromPart(part) && partIndex < parts.length) {
            goNextPart(partIndex);
          }
          break;
        }
      }
    }, [goPreviousPart, goNextPart, parts]);
    const { formProp, formState } = useForm({
      parts: boundValueArray
    });
    React.useEffect(() => {
      var _a2;
      setBoundValue == null ? void 0 : setBoundValue((_a2 = formState == null ? void 0 : formState.parts) == null ? void 0 : _a2.join(""));
    }, [formState]);
    useDidUpdateEffect(() => {
      if (globals.autoValidate && bindConfig.isTouched) {
        bindConfig.validate();
      }
    }, [boundValue]);
    const onBlur = React.useCallback(() => {
      if ((formState == null ? void 0 : formState.parts.every((p) => p)) && globals.autoValidate && !bindConfig.isTouched) {
        bindConfig.validate();
        bindConfig.setTouched(true);
      }
    }, [bindConfig, formState == null ? void 0 : formState.parts, globals.autoValidate]);
    const showLeftOverlay = leftOverlay && (globals.inputStatusPosition !== "left" || !globals.hideInputErrorIconOnStatus || !pending && !bindConfig.shouldShowValidationErrorIcon);
    const showRightOverlay = rightOverlay && (globals.inputStatusPosition !== "right" || !globals.hideInputErrorIconOnStatus || !pending && !bindConfig.shouldShowValidationErrorIcon);
    return jsxs(Fragment, { children: [jsxs("div", { ref, title: "Code input", ...nativeProps, children: [label && jsx(Label, { className: concat("arm-code-input-label", labelClassName), id: labelId, required, requiredIndicator: globals.requiredIndicator, displaySize: globals.inputDisplaySize, children: label }), jsx("div", { className: concat("arm-code-input", className), children: jsx(StatusWrapper, { error: error || !!bindConfig.validationErrorMessages.length, errorIcon: bindConfig.validationErrorIcon, statusPosition: globals.inputStatusPosition, pending, validationMode: bindConfig.validationMode, className: statusClassName, children: jsxs(Fragment, { children: [showLeftOverlay && leftOverlay, parts == null ? void 0 : parts.map((part, index) => jsx(CodeInputPart, { type: "text", bind: formProp("parts", index).bind(), part, onChange: (event) => onPartValueChange(event, index), onKeyDown: (event) => onKeyDown(event, index, +part), onPaste, onBlur, disabled: disabled || pending && globals.disableControlOnPending, ref: (r) => {
      inputRefs.current[index] = r;
    }, displaySize: globals.inputDisplaySize }, index)), showRightOverlay && rightOverlay] }) }) })] }), !!((_a = bindConfig.validationErrorMessages) == null ? void 0 : _a.length) && bindConfig.shouldShowValidationErrorMessage && jsx(ValidationErrors, { className: validationErrorsClassName, validationErrors: bindConfig.validationErrorMessages, scrollIntoView: globals.scrollValidationErrorsIntoView })] });
  }
  // type assertion to ensure generic works with RefForwarded component
  // DO NOT CHANGE TYPE WITHOUT CHANGING THIS, FIND TYPE BY INSPECTING React.forwardRef
);
CodeInput.displayName = "Code Input";
export {
  CodeInput
};
//# sourceMappingURL=codeInput.component.js.map
