import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useCallback, useMemo, useRef } from 'react';
import Button from '../Button';
import Icon, { registerIcon } from '../Icon';
import * as plusIcon from '../Icon/icons/plus.icon';
import * as minusIcon from '../Icon/icons/minus.icon';
import Flex from '../Flex';
import FormField from '../FormField';
import { useArrows, useConfiguration, useConsolidatedRef, useI18n, useInputFormatter, useTestIds, useUID } from '../../hooks';
import Input from '../Input';
import { withTestIds, createClassName } from '../../utils';
import VisuallyHiddenText from '../VisuallyHiddenText';
import StyledNumberInput, { StyledStepperInput } from './NumberInput.styles';
import { NUMBER_MAX_DECIMAL_PLACES } from './NumberInput.types';
import { getCleanedValue, getDecimalSign, getDecrementedValue, getFormattedValue, getIncrementedValue } from './utils';
import { getNumberInputTestIds } from './Number.test-ids';
registerIcon(minusIcon, plusIcon);
export default withTestIds(forwardRef(function NumberInput(props, ref) {
    const uid = useUID();
    const { testId, id = uid, status, label, labelHidden, info, required, disabled, readOnly, additionalInfo, value = '', onChange, onFocus, onBlur, onResolveSuggestion, unit, showDecimal = true, numberOfDecimals: numberOfDecimalsProp = NUMBER_MAX_DECIMAL_PLACES, showGroupSeparators = true, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER, step = 1, variant, 'aria-label': ariaLabel, autoFocus, className: classNameProp, ...restProps } = props;
    const className = createClassName('number-input', classNameProp);
    const testIds = useTestIds(testId, getNumberInputTestIds);
    const { locale } = useConfiguration();
    const inputRef = useConsolidatedRef(ref);
    const containerRef = useRef(null);
    const maximumFractionDigits = showDecimal ? numberOfDecimalsProp : 0;
    const decimalSign = useMemo(() => getDecimalSign(locale, { numberingSystem: 'latn' }), [locale]);
    const formatValue = useCallback((number) => {
        return getFormattedValue(number, locale, unit, {
            useGrouping: showGroupSeparators,
            maximumFractionDigits
        });
    }, [locale, unit, showGroupSeparators, maximumFractionDigits]);
    const onInputChange = (e) => {
        const inputValue = e.target.value;
        const cleanedInputValue = getCleanedValue(inputValue, decimalSign, maximumFractionDigits);
        if (cleanedInputValue === value)
            return;
        onChange(cleanedInputValue);
    };
    const onKeyDown = (e) => {
        if (readOnly)
            return;
        const input = e.currentTarget;
        switch (e.key) {
            case 'ArrowUp':
                e.preventDefault();
                onChange(getIncrementedValue(value, min, max, step));
                break;
            case 'ArrowDown':
                e.preventDefault();
                onChange(getDecrementedValue(value, min, max, step));
                break;
            case decimalSign:
                if (input.value.includes(decimalSign)) {
                    if (input.selectionStart &&
                        input.value.charAt(input.selectionStart) === decimalSign) {
                        input.setSelectionRange(input.selectionStart + 1, input.selectionStart + 1);
                    }
                    e.preventDefault();
                }
                else if (input.selectionStart !== null &&
                    !input.value.charAt(input.selectionStart - 1).match(/\d/)) {
                    onChange(getCleanedValue(`${input.value}0${decimalSign}`, decimalSign, maximumFractionDigits));
                    e.preventDefault();
                }
                break;
            case '-':
                if (value.startsWith('-'))
                    e.preventDefault();
                break;
            default:
        }
    };
    const onInputFocusEvent = (e) => {
        let parsableValue = value;
        if (Number.isNaN(parseFloat(value))) {
            parsableValue = '';
        }
        else if (value?.endsWith('.')) {
            parsableValue = value.slice(0, -1);
        }
        if (e.type === 'focus')
            onFocus?.(parsableValue);
        else {
            if (parsableValue !== value) {
                onChange(parsableValue);
            }
            onBlur?.(parsableValue);
        }
    };
    const formattedValue = useInputFormatter(inputRef, value, formatValue, `0-9${decimalSign}-`);
    const t = useI18n();
    const numberInput = (_jsx(Flex, { "data-testid": testIds.root, as: StyledNumberInput, hasSuggestion: status === 'pending' && !!onResolveSuggestion, container: { alignItems: 'center', wrap: 'nowrap' }, status: status, readOnly: readOnly, disabled: disabled, children: _jsx(Input, { "data-testid": testIds.control, onChange: onInputChange, id: id, readOnly: readOnly, required: required, disabled: disabled, ...restProps, className: className, inputMode: 'numeric', ref: inputRef, value: formattedValue, onKeyDown: onKeyDown, onFocus: onInputFocusEvent, onBlur: onInputFocusEvent, "aria-label": `${ariaLabel || label}. ${unit ? t('measured_in', [unit]) : ''}`, autoFocus: autoFocus, "aria-describedby": info ? `${id}-info` : undefined }) }));
    const onMinusClick = () => {
        onChange?.(getDecrementedValue(value, min, max, step));
    };
    const onPlusClick = () => {
        onChange?.(getIncrementedValue(value, min, max, step));
    };
    const elementRef = useRef(null);
    useArrows(elementRef, {
        cycle: false,
        selector: 'button, input',
        dir: 'left-right',
        initialFocusElement: inputRef.current
    });
    const stepper = (_jsxs(Flex, { as: StyledStepperInput, hasSuggestion: status === 'pending' && !!onResolveSuggestion, container: { alignItems: 'center', wrap: 'nowrap' }, status: status, readOnly: readOnly, disabled: disabled, size: formattedValue?.length, ref: elementRef, role: 'application', children: [_jsx(Button, { "data-testid": testIds.minus, variant: 'simple', icon: true, onClick: onMinusClick, readOnly: readOnly, disabled: disabled, tabIndex: -1, "aria-label": t('decrease_value_by', [step]), children: _jsx(Icon, { name: 'minus' }) }), _jsx(Flex, { container: { alignItems: 'center', wrap: 'nowrap', justify: 'center' }, item: { grow: 1 }, ref: containerRef, onClick: (e) => {
                    if (e.target === containerRef.current)
                        inputRef?.current?.focus();
                }, children: _jsx(Input, { "data-testid": testIds.control, onChange: onInputChange, id: id, readOnly: readOnly, required: required, disabled: disabled, ...restProps, className: className, inputMode: 'numeric', ref: inputRef, value: formattedValue, onKeyDown: onKeyDown, onFocus: onInputFocusEvent, onBlur: onInputFocusEvent, "aria-label": `${ariaLabel || label}. ${unit ? t('measured_in', [unit]) : ''}`, maxLength: 16, autoFocus: autoFocus, "aria-describedby": info ? `${id}-info ${id}-input-description` : `${id}-input-description` }) }), _jsx(Button, { "data-testid": testIds.plus, variant: 'simple', icon: true, onClick: onPlusClick, readOnly: readOnly, disabled: disabled, tabIndex: -1, "aria-label": t('increase_value_by', [step]), children: _jsx(Icon, { name: 'plus' }) }), _jsx(VisuallyHiddenText, { id: `${id}-input-description`, children: t('stepper_instructions') }), _jsx(VisuallyHiddenText, { "aria-live": 'polite', children: formattedValue })] }));
    const Comp = variant === 'stepper' && !readOnly ? stepper : numberInput;
    return label ? (_jsx(FormField, { testId: testIds, label: label, labelHidden: labelHidden, id: id, info: info, status: status, required: required, readOnly: readOnly, disabled: disabled, additionalInfo: additionalInfo, onResolveSuggestion: onResolveSuggestion, children: Comp })) : (Comp);
}), getNumberInputTestIds);
//# sourceMappingURL=NumberInput.js.map