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

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

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

import {
    NumberFormat,
    CountryCode,
    parsePhoneNumber,
    isValidPhoneNumber,
    AsYouType,
} from 'libphonenumber-js';

/**
 * parsePhoneNumber module Documentation:
 *
 */

/**
 * Overview of Component:
 *
 *  INPUTS:
 *          1. input passed in from another component will always be in the international format with a leading '+' symbol and country code:  +15553334444
 *          2. input typed by the user is restricted to only numbers and must include the country code. We provide the '+' symbol
 *
 *  OUTPUTS:
 *          1. output will always be the same as the input: +15553334444
 *
 *  PURPOSE of This Component: Validate, Format, and Display the phone number in the international format (with the option of customizing the format).
 */

/**
 * UPDATES TO MAKE:
 *
 *                  1. Optimize to avoid unnecessary re-renders
 *                  2. Evaluate the performance of the AsYouType Constructor
 *
 */

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

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

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

    /** country */
    country: CountryCode;

    /** Format allows developer to choose which formatting style (i.e. international, national, etc) */
    format: NumberFormat;

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

const _PhoneNumberPicker = (
    props: PhoneNumberPickerProps,
    ref: ForwardedRef<HTMLDivElement>,
): JSX.Element => {
    const {
        id,
        value,
        defaultValue,
        onChange = () => null,
        placeholder,
        country = 'US',
        format = 'NATIONAL',
        inputProps = {},
        disabled = false,
        onBlur = () => null,
        onFocus = () => null,
        ...otherProps
    } = props;

    // track the input value
    const inputRef = useRef<HTMLInputElement>(null);

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

    // managing input formatted as a phone number (###) ###-####
    const [phoneNumberValue, setPhoneNumberValue] = useState('');
    const [isFocused, setIsFocused] = useState(false);

    // handle input passed into the component
    useEffect(() => {
        let updated = '';
        if (
            internalValue === undefined ||
            internalValue === null ||
            internalValue === '' ||
            !isValidPhoneNumber(internalValue)
        ) {
            // noop
        } else if (isFocused) {
            // if in focus and is a valid phone number, show parsedPhoneNumber
            updated = parsePhoneNumber(internalValue, country).format('E.164');
            // on blur
        } else {
            // parse number
            // validate phone number
            updated = parsePhoneNumber(internalValue, country).format(format); // since we checked that the internalValue cannot be undefined, null, or empty, AND IS a valid phone number, this should never be undefined.
        }

        // do not update the phoneNumberValue if isFocused (assumption is user is not finished typing)
        if (isFocused) {
            return;
        }

        setPhoneNumberValue(updated);
    }, [isFocused, country, format, internalValue]);

    return (
        <StyledFormInput
            ref={ref}
            focusRef={inputRef}
            disabled={disabled}
            {...otherProps}
        >
            <StyledInput
                ref={inputRef}
                id={id}
                type={'text'}
                value={phoneNumberValue}
                placeholder={placeholder}
                disabled={disabled}
                onChange={(event) => {
                    if (disabled) {
                        return;
                    }

                    if (event.target.value === '' || !event.target.value) {
                        return;
                    }

                    // create a new asYouType
                    const asYouType = new AsYouType(country);

                    // does not format as you type, but does provide the leading '+1'
                    asYouType.input(event.target.value);

                    // get the formatted value
                    const formattedPhoneNumber =
                        asYouType.getNumberValue() || event.target.value;

                    // update input with formatted phone number value
                    setPhoneNumberValue(formattedPhoneNumber);

                    // only update the internal value if it is a valid number
                    if (!isValidPhoneNumber(formattedPhoneNumber)) {
                        if (internalValue === null) {
                            return;
                        }

                        setInternalValue(null);
                        return;
                    }

                    // update internalValue
                    setInternalValue(formattedPhoneNumber);
                }}
                onBlur={(event) => {
                    // pass the blur event
                    onBlur(event);
                    setIsFocused(false);
                }}
                onFocus={(event) => {
                    // pass the focus event
                    onFocus(event);
                    setIsFocused(true);
                }}
                {...inputProps}
            />
        </StyledFormInput>
    );
};

export const PhoneNumberPicker = forwardRef(_PhoneNumberPicker) as (
    props: PhoneNumberPickerProps & {
        ref?: ForwardedRef<HTMLDivElement>;
    },
) => ReturnType<typeof _PhoneNumberPicker>;
