import {
    ComponentPropsWithRef,
    forwardRef,
    ForwardedRef,
    RefObject,
    useRef,
    useState,
    useEffect,
} from 'react';

import { InputOptions } from '../../utility';

export const BaseFormInputStyles = {
    backgroundColor: `$base`,
    borderWidth: '$default',
    borderRadius: '$default',
    boxShadow: 'none',
    display: 'inline-block',
    color: '$grey-1',
    fontSize: '$sm',
    lineHeight: '$none',
    margin: '0',
    outline: 'none',
    userSelect: 'none',
    overflow: 'hidden',
    width: '$space$full',
    '&[data-disabled]': {
        backgroundColor: `$grey-5`,
        borderColor: `$grey-4`,
        color: `$grey-3`,
        cursor: 'default',
        pointerEvents: 'none',
    },
    '&:focus': {
        outline: '1px solid $primary-1',
        borderColor: '$primary-1',
    },
    '&[data-focused]': {
        outline: '1px solid $primary-1',
        borderColor: '$primary-1',
    },
    variants: {
        valid: {
            true: {
                borderColor: `$grey-4`,
                '&:hover': {
                    borderColor: '$primary-1',
                },
            },
            false: {
                borderColor: `$error-1`,
            },
        },
        size: {
            sm: {
                padding: '0 $1',
                height: '$space$6',
            },
            md: {
                padding: '$1 $2',
                height: '$space$8',
            },
            lg: {
                padding: '$2 $3',
                height: '$space$10',
            },
        },
    },
    defaultVariants: {
        size: 'md',
        valid: true,
    },
};

export interface BaseFormInputProps<V>
    extends InputOptions<V>,
        Omit<
            ComponentPropsWithRef<'div'>,
            'value' | 'defaultValue' | 'defaultChecked' | 'onChange'
        > {
    /** Set the size of the FormInput */
    size?: 'sm' | 'md' | 'lg';
}

interface _BaseFormInputProps<V>
    extends Omit<BaseFormInputProps<V>, 'value' | 'defaultValue' | 'onChange'> {
    /** ref to pass the focus to  */
    focusRef: RefObject<HTMLElement> | null;
}

/**
 * Wrap form input fields
 */
export const _BaseFormInput = <V,>(
    props: _BaseFormInputProps<V>,
    ref: ForwardedRef<HTMLDivElement>,
): JSX.Element => {
    const {
        id,
        children,
        disabled = false,
        focusRef,
        onClick = () => null,
        onFocus = () => null,
        onBlur = () => null,
        ...otherProps
    } = props;

    // state
    const [focused, setFocused] = useState<boolean>(false);

    // refs
    const inputRef = useRef<HTMLDivElement | null>(null);

    useEffect(() => {
        if (
            document.hasFocus() &&
            inputRef.current &&
            inputRef.current.contains(document.activeElement)
        ) {
            setFocused(true);
        }
    }, []);

    return (
        <div
            ref={(node) => {
                inputRef.current = node;
                if (typeof ref === 'function') {
                    ref(node);
                } else if (ref) {
                    ref.current = node;
                }
            }}
            id={id}
            data-disabled={disabled || undefined}
            data-focused={focused || undefined}
            onClick={(event) => {
                onClick(event);

                // move the focus if it isn't focused
                if (!focused) {
                    focusRef?.current?.focus();
                }
            }}
            onFocus={(event) => {
                onFocus(event);

                // show the focus when it enters this or a child
                if (!event.currentTarget.contains(event.relatedTarget)) {
                    // show the focus
                    setFocused(true);
                }
            }}
            onBlur={(event) => {
                onBlur(event);

                // hide the focus when it leaves this or a child
                if (!event.currentTarget.contains(event.relatedTarget)) {
                    // hide the focus
                    setFocused(false);
                }
            }}
            {...otherProps}
        >
            {children}
        </div>
    );
};

export const BaseFormInput = forwardRef(_BaseFormInput) as <V>(
    props: _BaseFormInputProps<V> & {
        ref?: ForwardedRef<HTMLDivElement>;
    },
) => ReturnType<typeof _BaseFormInput>;
