import React, { forwardRef, useState, useCallback, useEffect } from 'react';
import { Form } from '@orchard/frontend-react-components';
import accounting from 'accounting';
import { round } from 'lodash';
import { NUMBER_FORMATS } from '../../constants';
import useIdentity from '../../utils/use-identity';

type Props = React.ComponentProps<typeof Form.Control> & {
    value: number | null;
    onChange(value: number | null): void;
    onBlur?(): void;
    forwardedRef?: ((instance: HTMLInputElement | null) => void) | React.MutableRefObject<HTMLInputElement | null> | null;
    placeholder?: string | number;
    maxValue?: number;
    decimalPlaces?: number;
    forceDecimalPlaces?: boolean;
    allowZero?: boolean;
};

const NumberInput: React.FC<Props> = ({
    value,
    onChange,
    onBlur,
    maxValue,
    forwardedRef,
    placeholder,
    decimalPlaces = 2,
    forceDecimalPlaces,
    allowZero = true,
    ...forwardedProps
}) => {
    const { numberFormat } = useIdentity();

    const [inputValue, setInputValue] = useState<string>('');

    const formatAmount = useCallback((amount: number | null) => {
        if (amount === null)
            return '';

        const precision = forceDecimalPlaces || !Number.isInteger(amount) ? decimalPlaces : 0;

        return numberFormat !== NUMBER_FORMATS.US
            ? accounting.formatNumber(amount, precision, '.', ',')
            : accounting.formatNumber(amount, precision, ',', '.');
    }, [decimalPlaces, forceDecimalPlaces, numberFormat]);

    const parseAmount = useCallback((input: string) => {
        const transformedInput = numberFormat !== NUMBER_FORMATS.US ? input.replace(/,/g, '.') : input;
        const amount = parseFloat(transformedInput);
        const roundedAmount = round(amount, decimalPlaces);

        if (Number.isNaN(roundedAmount)
                || (!allowZero && roundedAmount === 0)
                || (maxValue && roundedAmount > maxValue))
            return null;

        return roundedAmount;
    }, [allowZero, decimalPlaces, maxValue, numberFormat]);

    useEffect(() => {
        if (value !== parseAmount(inputValue))
            setInputValue(
                typeof value === 'number' && !Number.isNaN(value)
                    ? formatAmount(value)
                    : ''
            );
    }, [value, inputValue, parseAmount, formatAmount]);

    const formattedPlaceholder = typeof placeholder === 'number' ? formatAmount(placeholder) : placeholder;

    const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = ({ target: { value: newInputValue } }) => {
        const sanitizedInputValue = numberFormat !== NUMBER_FORMATS.US
            ? newInputValue.replace(/[^,\d]/g, '')
            : newInputValue.replace(/[^.\d]/g, '');

        setInputValue(sanitizedInputValue);
        onChange(parseAmount(sanitizedInputValue));
    };

    const handleInputBlur = () => {
        setInputValue(formatAmount(value));
        if (onBlur) onBlur();
    };

    return (
        <Form.Control
            { ...forwardedProps }
            type="text"
            value={ inputValue }
            placeholder={ formattedPlaceholder }
            onChange={ handleInputChange }
            onBlur={ handleInputBlur }
            ref={ forwardedRef }
        />
    );
};

export default forwardRef<HTMLInputElement, Props>((props, ref) => (
    <NumberInput { ...props } forwardedRef={ ref } />
));
