import React, { useEffect, useCallback } from 'react';
import Input from '../Input';
import { defaultValueToNullIfEmpty, isInRange, validateValue, normalizeValues, getClosestValue, } from './utils';
import { dataHooks } from './constants';
import deprecationLog from '../utils/deprecationLog';
const NumberInput = ({ suffix, defaultValue, strict = false, max, min, hideStepper = false, value: givenValue, inputRef, step = 1, status, invalidMessage, statusMessage, onChange, onInvalid, onKeyDown, type, disabled, ...props }) => {
    const initialState = {
        hasError: false,
        localValue: '',
    };
    const [state, setState] = React.useReducer((currentState, newState) => ({
        ...currentState,
        ...newState,
    }), initialState);
    const [inputDOM, setInputDOM] = React.useState(null);
    const shouldShowError = state.hasError && invalidMessage;
    const isControlled = givenValue !== undefined && givenValue !== null && onChange;
    const hasPredefinedSteps = Array.isArray(step);
    const getCurrentValue = () => {
        const currentValue = isControlled ? givenValue : state.localValue;
        return Number(currentValue);
    };
    const getStepperDisabledState = () => {
        if (disabled) {
            return [true, true];
        }
        const currentValue = getCurrentValue();
        if (hasPredefinedSteps) {
            const sortedSteps = [...step].sort((a, b) => a - b);
            const minStep = sortedSteps[0];
            const maxStep = sortedSteps[sortedSteps.length - 1];
            const effectiveMin = min !== undefined ? Math.max(min, minStep) : minStep;
            const effectiveMax = max !== undefined ? Math.min(max, maxStep) : maxStep;
            return [currentValue >= effectiveMax, currentValue <= effectiveMin];
        }
        return [
            max !== undefined && currentValue >= max,
            min !== undefined && currentValue <= min,
        ];
    };
    const [isUpDisabled, isDownDisabled] = getStepperDisabledState();
    const setValueAndValidate = ({ value, shouldCallOnChangeCallback = true, }) => {
        const { numberValue, stringValue } = normalizeValues(value);
        const { hasError, validationType } = validateValue({
            value: stringValue,
            minValue: min,
            maxValue: max,
        });
        setState({
            hasError,
            localValue: stringValue,
        });
        const isControlledValidNumber = isControlled && !Number.isNaN(numberValue);
        if (hasError ? isControlledValidNumber : shouldCallOnChangeCallback) {
            onChange?.(numberValue, stringValue);
        }
        if (hasError) {
            onInvalid?.(stringValue, {
                validationType,
                value: stringValue,
            });
        }
    };
    useEffect(() => {
        if (strict) {
            deprecationLog('<NumberInput/> - prop strict is deprecated and not needed anymore. By using min and max it will enforce strict both for the ticker and input automatically.');
        }
    }, [strict]);
    useEffect(() => {
        const newLocalValue = defaultValueToNullIfEmpty(givenValue, defaultValue);
        setValueAndValidate({
            value: newLocalValue,
            shouldCallOnChangeCallback: false,
        });
        // TODO: fix ESLint error
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [givenValue, defaultValue]);
    const increment = () => {
        const currentValue = getCurrentValue();
        const stringValue = String(currentValue || inputDOM?.value || '').replace(',', '.');
        const numberValue = parseFloat(stringValue) || 0;
        const updatedValue = hasPredefinedSteps
            ? getClosestValue(numberValue, step, 'up')
            : Number((numberValue + step).toPrecision(12));
        if (isInRange({ value: updatedValue, minValue: min, maxValue: max })) {
            setValueAndValidate({ value: updatedValue });
        }
        else if (min !== undefined && updatedValue <= min) {
            setValueAndValidate({ value: min });
        }
        inputDOM?.focus();
    };
    const decrement = () => {
        const currentValue = getCurrentValue();
        const stringValue = String(currentValue || inputDOM?.value || '').replace(',', '.');
        const numberValue = parseFloat(stringValue) || 0;
        const updatedValue = hasPredefinedSteps
            ? getClosestValue(numberValue, step, 'down')
            : Number((numberValue - step).toPrecision(12));
        if (isInRange({ value: updatedValue, minValue: min, maxValue: max })) {
            setValueAndValidate({ value: updatedValue });
        }
        else if (max !== undefined && updatedValue >= max) {
            setValueAndValidate({ value: max });
        }
        inputDOM?.focus();
    };
    const getInputRef = useCallback((inputElement) => {
        setInputDOM(inputElement);
        if (typeof inputRef === 'function') {
            inputElement && inputRef(inputElement);
        }
        else if (inputRef && 'current' in inputRef) {
            inputRef.current = inputElement;
        }
    }, [inputRef]);
    const getStatusMessage = () => {
        if (shouldShowError) {
            return invalidMessage;
        }
        return statusMessage;
    };
    const onInputValueChange = (event) => {
        const { value } = event.target;
        // matches numbers, i.e. 123 or -123
        if (/^-?\d*[.,]?\d*$/.test(value)) {
            setValueAndValidate({ value });
        }
        else {
            setState({ localValue: state.localValue });
        }
    };
    const incrementOrDecrementValue = (e) => {
        if (e.key === 'ArrowUp') {
            increment();
            e.preventDefault();
        }
        if (e.key === 'ArrowDown') {
            decrement();
            e.preventDefault();
        }
        onKeyDown?.(e);
    };
    const shouldRenderGivenValue = isControlled &&
        // to catch values like 0. or 1. and so on and so on
        givenValue !== Number(state.localValue) &&
        // to allow entering a negative number
        state.localValue !== '-';
    return (React.createElement(Input, { ...props, disabled: disabled, max: max, min: min, type: type || 'text', value: shouldRenderGivenValue ? givenValue : state.localValue, onChange: onInputValueChange, inputRef: getInputRef, status: shouldShowError ? 'error' : status, statusMessage: getStatusMessage(), onKeyDown: incrementOrDecrementValue, ariaRoledescription: "spin button", inputmode: "numeric", suffix: suffix || !hideStepper ? (React.createElement(Input.Group, null,
            suffix,
            !hideStepper && (React.createElement(Input.Ticker, { onUp: increment, onDown: decrement, dataHook: dataHooks.numberInputTicker, upDisabled: isUpDisabled, downDisabled: isDownDisabled, onMouseDown: e => e.preventDefault() })))) : undefined }));
};
NumberInput.displayName = 'NumberInput';
export default NumberInput;
//# sourceMappingURL=NumberInput.js.map