import { jsx as _jsx } from "react/jsx-runtime";
// (C) 2007-2026 GoodData Corporation
import { memo, useEffect, useRef, useState } from "react";
import { memoize } from "lodash-es";
import { InputPure } from "./InputPure.js";
import { DEFAULT_SEPARATORS, formatNumberWithSeparators } from "./numberFormat.js";
// Highest number (BIGINT) according to gooddata documentation help.gooddata.com object-datatypes
export const MAX_NUMBER = 10 ** 15;
// Max number of digits right to decimal point according to gooddata documentation help.gooddata.com object-datatypes
const MAX_DECIMAL_POINT_NUMBERS = 6;
const getDanglingDecimalPointRegExp = memoize((decimal) => new RegExp(`\\${decimal}$`));
const getFormatValidationRegExp = memoize(({ thousand, decimal }) => new RegExp(`^-?(\\d|\\${thousand})*(\\${decimal}\\d*)?$`));
const parseStandardNumberString = (numberString) => {
    const belowDecimal = numberString.split(".")[1];
    const roundedNumberString = belowDecimal && belowDecimal.length >= MAX_DECIMAL_POINT_NUMBERS
        ? parseFloat(numberString).toFixed(MAX_DECIMAL_POINT_NUMBERS)
        : numberString;
    const number = parseFloat(roundedNumberString);
    return number === 0 ? 0 : number;
};
// Removes thousand separators while keeping the decimal separator intact, so the value can be
// edited as plain digits (no separators popping in/out) while the input is focused.
const removeThousandSeparators = (value, { thousand } = DEFAULT_SEPARATORS) => value.split(thousand).join("");
const convertFormattedStringToStandard = (formattedString, { thousand, decimal }) => {
    const withoutThousandSeparators = formattedString.toString().split(thousand).join("");
    const withoutDanglingDecimalPoint = withoutThousandSeparators.replace(getDanglingDecimalPointRegExp(decimal), "");
    const withStandardDecimalPoint = withoutDanglingDecimalPoint.split(decimal).join(".");
    return withStandardDecimalPoint.length > 0 ? withStandardDecimalPoint : null;
};
const parse = (value, separators = DEFAULT_SEPARATORS) => {
    if (value === null || value === "" || value === "-") {
        return null;
    }
    const numberString = convertFormattedStringToStandard(value, separators);
    if (numberString === null) {
        return null;
    }
    return parseStandardNumberString(numberString);
};
const isValid = (value, separators = DEFAULT_SEPARATORS) => {
    const parsed = parse(value, separators);
    return getFormatValidationRegExp(separators).test(value) && Math.abs(parsed ?? 0) <= MAX_NUMBER;
};
// Coerces input value to number for formatting (handles string/number/null/undefined)
const toNumberValue = (value) => {
    if (value === null || value === undefined || value === "") {
        return null;
    }
    return typeof value === "number" ? value : Number(value);
};
/**
 * @internal
 */
export const InputWithNumberFormat = memo(function InputWithNumberFormat({ separators, value: propValue, onChange, onFocus, onBlur, ...restProps }) {
    const inputRef = useRef(null);
    const [value, setValue] = useState(() => formatNumberWithSeparators(toNumberValue(propValue), separators));
    const [isFocused, setIsFocused] = useState(false);
    useEffect(() => {
        if (!isFocused) {
            setValue(formatNumberWithSeparators(toNumberValue(propValue), separators));
        }
    }, [propValue, isFocused, separators]);
    const handleCaretShift = (e) => {
        const caretPosition = (e.target.selectionStart ?? 1) - 1;
        // Use setTimeout to ensure the DOM has updated before setting selection
        setTimeout(() => {
            inputRef.current?.inputNodeRef?.setSelectionRange(caretPosition, caretPosition);
        }, 0);
    };
    const handleChange = (newValue, e) => {
        if (value === newValue) {
            return;
        }
        if (!isValid(newValue, separators)) {
            handleCaretShift(e);
            return;
        }
        setValue(newValue);
        const parsedValue = parse(newValue, separators);
        onChange?.(parsedValue);
    };
    const handleFocus = (e) => {
        // Strip thousand separators on focus so the user edits plain digits instead of having to
        // navigate/delete through baked-in separators. They are re-applied on blur.
        setValue(removeThousandSeparators(value, separators));
        setIsFocused(true);
        onFocus?.(e);
    };
    const handleBlur = (e) => {
        setValue(formatNumberWithSeparators(parse(value, separators), separators));
        setIsFocused(false);
        onBlur?.(e);
    };
    return (_jsx(InputPure, { ...restProps, ref: (ref) => {
            inputRef.current = ref;
        }, onFocus: handleFocus, onChange: handleChange, onBlur: handleBlur, value: value }));
});
