"use client";
import { useRef, useImperativeHandle, useState, useMemo, useEffect } from "react";
import { numberToBigInt } from "../../utils/numberToBigInt.js";
import { formatBigInt } from "../../utils/formatBigInt.js";
import { parseBigInt } from "../../utils/parseBigInt.js";
import { bigIntToNumber } from "../../utils/bigIntToNumber.js";
import { usePrevious } from "../../hooks/usePrevious.js";
function useNumberInput({
  ref,
  useBigInt = false,
  precision = 18n,
  defaultValue = null,
  value: controlledValue,
  onChange: controlledOnChange,
  min: passedMin = null,
  max: passedMax = null,
  required = false,
  format = {}
} = {}) {
  if (passedMin !== null && passedMax !== null && passedMin > passedMax) {
    throw new Error("useNumberInput: `min` cannot be greater than `max`");
  }
  const innerRef = useRef(null);
  useImperativeHandle(ref, () => innerRef.current);
  const normalizeValue = (value) => {
    if (typeof value === "number") {
      return numberToBigInt(value, precision);
    } else if (typeof value === "bigint") {
      return value;
    } else {
      return null;
    }
  };
  const denormalizeValue = (value) => {
    if (value === null) {
      return null;
    } else if (useBigInt) {
      return value;
    } else {
      return bigIntToNumber(value, precision);
    }
  };
  const formatValue = (value, options = format) => {
    if (value === null) return "";
    return formatBigInt(value, precision, options);
  };
  const defaultMin = useBigInt ? null : Number.MIN_SAFE_INTEGER;
  const defaultMax = useBigInt ? null : Number.MAX_SAFE_INTEGER;
  const min = normalizeValue(passedMin ?? defaultMin);
  const max = normalizeValue(passedMax ?? defaultMax);
  const allowNegative = min === null || min < 0n;
  const allowDecimals = precision > 0n;
  const validateValue = (value) => {
    if (value === null) {
      if (required) {
        return "EMPTY";
      }
      return null;
    }
    if (min !== null && value < min) {
      return "BELOW_MIN";
    }
    if (max !== null && value > max) {
      return "ABOVE_MAX";
    }
    return null;
  };
  const defaultNormalizedValue = normalizeValue(controlledValue !== void 0 ? controlledValue : defaultValue);
  const defaultError = validateValue(defaultNormalizedValue);
  const defaultValidatedValue = !defaultError ? defaultNormalizedValue : null;
  const [inputValue, setInputValue] = useState(formatValue(defaultNormalizedValue));
  const [validatedValue, setValidatedValue] = useState(defaultValidatedValue);
  const [error, setError] = useState(defaultError !== "EMPTY" ? defaultError : null);
  const [nonValidatedValue, setNonValidatedValue] = useState(defaultNormalizedValue ?? 0n);
  let nearestValidValue = nonValidatedValue;
  if (min !== null && nonValidatedValue < min) {
    nearestValidValue = min;
  }
  if (max !== null && nonValidatedValue > max) {
    nearestValidValue = max;
  }
  const setValue = (value, updateInputValue = true) => {
    const normalizedValue = normalizeValue(value);
    const formattedValue = formatValue(normalizedValue);
    const valueError = validateValue(normalizedValue);
    if (updateInputValue) {
      setInputValue(formattedValue);
    }
    setNonValidatedValue(normalizedValue ?? 0n);
    if (normalizedValue === null || valueError) {
      setValidatedValue(null);
      controlledOnChange == null ? void 0 : controlledOnChange(null);
      setError(valueError);
      return;
    }
    const parsedValue = parseBigInt(formattedValue, precision);
    setValidatedValue(parsedValue);
    controlledOnChange == null ? void 0 : controlledOnChange(denormalizeValue(parsedValue));
    setError(null);
  };
  const previousControlledValue = usePrevious(controlledValue);
  if (controlledValue !== void 0 && previousControlledValue !== void 0 && controlledValue !== previousControlledValue && defaultValidatedValue !== validatedValue) {
    setValue(controlledValue);
  }
  const getSignificantCaretPositionMap = (string) => {
    const significantCaretPositionMap = [];
    let significantCaretPosition = 0;
    let hasDecimalPoint = false;
    let hasSignificantDigit = false;
    significantCaretPositionMap.push(significantCaretPosition);
    for (let i = 0; i < string.length; i++) {
      const character = string[i];
      if (character === "-" && significantCaretPosition === 0) {
        significantCaretPosition++;
      } else if (character === "." && !hasDecimalPoint) {
        significantCaretPosition++;
        hasDecimalPoint = true;
        if (string[i - 1] !== "0") {
          significantCaretPosition++;
        }
      } else if (/\d/.test(character)) {
        if (character === "0" && !hasSignificantDigit) {
          if (string[i - 1] === "." || string[i + 1] === ".") {
            significantCaretPosition++;
            hasSignificantDigit = true;
          }
        } else {
          significantCaretPosition++;
          hasSignificantDigit = true;
        }
      }
      significantCaretPositionMap.push(significantCaretPosition);
    }
    return significantCaretPositionMap;
  };
  const getSignificantCaretPosition = (input) => {
    const significantCaretPositionMap = getSignificantCaretPositionMap(input.value);
    return significantCaretPositionMap[input.selectionStart !== null && input.selectionStart < significantCaretPositionMap.length ? input.selectionStart : significantCaretPositionMap.length];
  };
  const setSignificantCaretPosition = (input, position) => {
    const significantCaretPositionMap = getSignificantCaretPositionMap(input.value);
    let actualCaretPosition = significantCaretPositionMap.indexOf(position);
    if (actualCaretPosition === -1) {
      actualCaretPosition = input.value.length;
    }
    input.setSelectionRange(actualCaretPosition, actualCaretPosition);
  };
  const previousSignificantCaretPosition = useRef(null);
  const [, setForceRerender] = useState({});
  const forceRerender = () => setForceRerender({});
  const charsToRemove = useMemo(
    () => new RegExp(`[^\\d,${allowNegative ? "-" : ""}${allowDecimals ? "." : ""}]`, "g"),
    [allowNegative, allowDecimals]
  );
  const onKeyDown = (event) => {
    const allowKeyPress = event.ctrlKey || event.metaKey || /\d/.test(event.key) || allowNegative && event.key === "-" || allowDecimals && event.key === "." || ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Tab"].includes(event.key);
    if (!allowKeyPress) {
      event.preventDefault();
    }
  };
  const onChange = (newInputValue) => {
    if (innerRef.current) {
      previousSignificantCaretPosition.current = getSignificantCaretPosition(innerRef.current);
    }
    let cleanedValue = newInputValue.replace(charsToRemove, "");
    if (allowNegative) {
      cleanedValue = cleanedValue.replace(/(?!^)-/g, "");
    }
    if (allowDecimals) {
      const cleanedValueParts = cleanedValue.split(".");
      cleanedValue = cleanedValueParts.length > 1 ? `${cleanedValueParts.shift()}.${cleanedValueParts.join("")}` : cleanedValueParts[0];
    }
    setInputValue(cleanedValue);
    if (cleanedValue === "") {
      setValue(null, false);
      return;
    }
    const parsedValue = parseBigInt(cleanedValue, precision);
    if (parsedValue === null) {
      setValidatedValue(null);
      controlledOnChange == null ? void 0 : controlledOnChange(null);
      setError("NOT_A_NUMBER");
      return;
    }
    setValue(parsedValue, false);
    if (!/^-0+$/.test(cleanedValue) && !/\.((.*)0+)?$/.test(cleanedValue)) {
      setInputValue(formatValue(parsedValue, { minimumFractionDigits: 0, maximumFractionDigits: 20 }));
    }
    forceRerender();
  };
  useEffect(() => {
    if (!innerRef.current || previousSignificantCaretPosition.current === null) return;
    const significantCaretPosition = getSignificantCaretPosition(innerRef.current);
    if (significantCaretPosition !== previousSignificantCaretPosition.current) {
      setSignificantCaretPosition(innerRef.current, previousSignificantCaretPosition.current);
    }
    previousSignificantCaretPosition.current = null;
  });
  const onBlur = () => {
    const parsedValue = parseBigInt(inputValue, precision);
    if (parsedValue !== null) {
      setInputValue(formatValue(parsedValue));
    }
  };
  const status = error ? "error" : void 0;
  let message;
  if (error) {
    switch (error) {
      case "NOT_A_NUMBER":
        message = "Please enter a valid number.";
        break;
      case "BELOW_MIN":
        message = `This field’s value cannot be less than ${formatValue(min)}.`;
        break;
      case "ABOVE_MAX":
        message = `This field’s value cannot be greater than ${formatValue(max)}.`;
        break;
      case "EMPTY":
        message = "This field is required.";
        break;
    }
  }
  return {
    value: denormalizeValue(validatedValue),
    setValue,
    error,
    nonValidatedValue,
    nearestValidValue: denormalizeValue(nearestValidValue),
    props: {
      ref: innerRef,
      type: "number",
      value: inputValue,
      onKeyDown,
      onChange,
      onBlur,
      required,
      status,
      message
    }
  };
}
export {
  useNumberInput
};
