import React, {
    useState,
    useRef,
    ForwardedRef,
    forwardRef,
    useMemo,
    useEffect,
} from 'react';
import { useValue } from '../../hooks';

import { styled } from '../../stitches.config';

import {
    BaseFormInputStyles,
    BaseFormInput,
    BaseFormInputProps,
} from '../internal';

/**
 * QUESTIONS:
 *
 * Should we set initialValue as null or 0?
 */
/**
 * REQUIREMENTS:
 *
 * input: needs to be a number
 *
 * output: needs to be a number
 *
 * display: string - currency format
 */

/**
 * OVERVIEW: This currency component allows an end user to ONLY type in numbers in the input field and displays the value as a currency: $0.00.
 *            If value is passed in then we will display value as currency on first render.
 *
 * HOW:
 * We use inputType state to toggle type between 'text' and 'number', as type must be set to 'text' for currencyValue state to display properly and
 *  'number' to gain browser validation(i.e. only 0-9, -, e can be entered) and for internalValue state to display properly.
 *
 * internalValue: defaults to 0 if no value is passed in, and is later updated onChange
 * placeholder: '$0.00'
 *
 * onChange: onChangeHandler is binded to the input element to manage the user input value. onChangeHandler does three things:
 *          1. checks if input element is disabled
 *          2. removes the leading 0
 *          3. updates internalValue state on every keystroke
 *
 * onBlur: updates the relevant input element attributes and state to display value as currencyValue. onBlur does three things:
 *          1. set input type to be 'text'
 *          2. set isCurrency flag to be true
 *          3. set value to be currencyValue
 *
 * onFocus: updates relevant input element attributes and state to display value as number:
 *          1. set inputType state to be 'number', so that the value will display as number
 *          2. set isCurrency flag to be false so that value will be internalValue
 */

/**
 * UPDATES TO MAKE:.
 *
 * pulled in the existing Input component's source code, rather than re-using Input component directly.
 *
 * potentially move logic into a custom hook
 */

const StyledFormInput = styled(BaseFormInput, BaseFormInputStyles, {
    cursor: 'text',
});

const StyledInput = styled('input', {
    backgroundColor: 'transparent',
    height: '$space$full',
    outline: 'none',
    width: '$space$full',
});

export interface CurrencyPickerProps extends BaseFormInputProps<number | null> {
    /** Placeholder to show when there is no value */
    placeholder?: string;

    /** Props to pass to the input */
    inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
}

const _CurrencyPicker = (
    props: CurrencyPickerProps,
    ref: ForwardedRef<HTMLDivElement>,
): JSX.Element => {
    // get and set the values
    const {
        id,
        value,
        defaultValue,
        onChange = () => null,
        placeholder,
        inputProps = {},
        disabled = false,
        onFocus = () => null,
        onBlur = () => null,
        ...otherProps
    } = props;

    // store input in a ref
    const inputRef = useRef<HTMLInputElement>(null);

    // manage internal value
    const [internalValue, setInternalValue] = useValue({
        initialValue: null,
        value: value,
        defaultValue: defaultValue,
        onChange: onChange,
    });

    // toggle the focus state to change the view
    const [isFocused, setFocused] = useState(false);

    // manage state of input formatted as currency i.e. $##.##
    const [currencyValue, setCurrencyValue] = useState('');

    // memoize the formatter
    const currencyFormatter = useMemo(() => {
        return new Intl.NumberFormat('en-US', {
            style: 'currency',
            currency: 'USD', // can make this dynamic by pulling user's context (location/currency etc)
        });
    }, []);

    // checks for input passed into component, formats and uses it to update currencyValue
    useEffect(() => {
        let updated = '';
        if (internalValue === undefined || internalValue === null) {
            // noop
        } else if (isFocused) {
            updated = String(internalValue);
        } else {
            updated = currencyFormatter.format(internalValue);
        }

        setCurrencyValue(updated);
    }, [internalValue, currencyFormatter, isFocused]);

    return (
        <StyledFormInput
            ref={ref}
            focusRef={inputRef}
            disabled={disabled}
            {...otherProps}
        >
            <StyledInput
                ref={inputRef}
                id={id}
                type={'text'}
                inputMode={'numeric'}
                value={currencyValue}
                disabled={disabled}
                onChange={(event) => {
                    // don't do anything
                    if (disabled) {
                        return;
                    }

                    // validate input prevent multiple decimal points
                    const cleanInput = event.target.value
                        .replace(/[^[0-9.]+/g, '')
                        .replace(/([^.]*\.[^.]*)\./g, '$1');

                    // update the input
                    setCurrencyValue(cleanInput);

                    // nothing is there ignore it
                    if (!cleanInput) {
                        // don't update if it is null
                        if (internalValue === null) {
                            return;
                        }

                        setInternalValue(null);
                        return;
                    }

                    // convert it to a number
                    let updated = Number(cleanInput);

                    // check for NaN, don't update if true
                    if (isNaN(updated)) {
                        return;
                    }

                    // round it
                    updated =
                        Math.round((updated + Number.EPSILON) * 100) / 100;

                    if (updated === internalValue) {
                        return;
                    }

                    // update internalValue
                    setInternalValue(updated);
                }}
                onBlur={(event) => {
                    // pass the blur event
                    onBlur(event);

                    // toggle the focus off
                    setFocused(false);
                }}
                onFocus={(event) => {
                    // pass the focus event
                    onFocus(event);

                    // set it as focused
                    setFocused(true);
                }}
                placeholder={placeholder}
                {...inputProps}
            />
        </StyledFormInput>
    );
};

export const CurrencyPicker = forwardRef(_CurrencyPicker) as (
    props: CurrencyPickerProps & {
        ref?: ForwardedRef<HTMLDivElement>;
    },
) => ReturnType<typeof _CurrencyPicker>;
