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

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

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

import { Icon } from '../Icon';

const buildCheckbox = (
    checked: NonNullable<CheckboxProps['value']>,
    valid: NonNullable<CheckboxProps['valid']>,
) => {
    let css = {};

    if (!checked) {
        css = {
            cursor: 'pointer',
            '&[data-disabled]': {
                backgroundColor: `$base`,
                borderColor: `$grey-4`,
                color: 'white',
                cursor: 'default',
                pointerEvents: 'none',
            },
        };

        if (valid === false) {
            css = {
                ...css,
                backgroundColor: '$base',
                borderColor: '$error-1',
                color: 'white',
                '&:hover': {
                    backgroundColor: '$error-5',
                    borderColor: '$error-1',
                },
            };
        } else {
            css = {
                ...css,
                backgroundColor: '$base',
                borderColor: '$grey-2',
                color: 'white',
                '&:hover': {
                    borderColor: '$primary-1',
                    backgroundColor: '$primary-5',
                },
            };
        }
    } else {
        css = {
            cursor: 'pointer',
            '&[data-disabled]': {
                backgroundColor: `$grey-4`,
                borderColor: `$grey-4`,
                color: 'white',
                cursor: 'default',
                pointerEvents: 'none',
            },
        };

        if (valid === false) {
            css = {
                ...css,
                backgroundColor: '$error-1',
                borderColor: '$error-1',
                color: `white`,
                '&:hover': {
                    backgroundColor: '$error-2',
                    borderColor: '$error-2',
                },
            };
        } else {
            css = {
                ...css,
                backgroundColor: '$primary-1',
                borderColor: '$primary-1',
                color: `white`,
                '&:hover': {
                    backgroundColor: '$primary-2',
                    borderColor: '$primary-2',
                },
            };
        }
    }
    return {
        checked,
        valid,
        css,
    };
};

const StyledLabel = styled('label', {
    cursor: 'pointer',
    display: 'flex',
    lineHeight: '$space$5',
    outline: 'none',
    position: 'relative',
    '&[data-disabled]': {
        cursor: 'default',
        pointerEvents: 'none',
    },
});

const StyledInput = styled('input', {
    margin: 0,
    opacity: 0,
    position: 'absolute',
    outline: 'none',
});

const StyledCheckbox = styled('div', {
    borderRadius: '$default',
    borderWidth: '$thick',
    height: '$space$5',
    display: 'inline-flex',
    flexShrink: '0',
    overflow: 'hidden',
    width: '$space$5',
    '&[data-focused]': {
        outline: '2px solid $primary-1',
        outlineOffset: '2px',
    },
    variants: {
        checked: {
            true: {},
            false: {},
        },
        valid: {
            true: {},
            false: {},
        },
    },
    compoundVariants: [
        buildCheckbox(true, true),
        buildCheckbox(true, false),
        buildCheckbox(false, true),
        buildCheckbox(false, false),
    ],
    defaultVariants: {
        checked: false,
        valid: true,
    },
});

const StyledText = styled('div', {
    color: `$grey-1`,
    fontSize: '$sm',
    margin: '0 0 0 $2',
    overflow: 'hidden',
});

export interface CheckboxProps
    extends InputOptions<boolean>,
        Omit<
            ComponentPropsWithRef<'label'>,
            'value' | 'defaultValue' | 'defaultChecked' | 'onChange'
        > {
    /** Mark the Checkbox in an indeterminate state. The box must be checked to show the indicator */
    indeterminate?: boolean;

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

// TODO: Need to figure out why @mdi/js isn't tree shaking
const mdiCheckBold =
    'M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z';
const mdiMinusThick = 'M20 14H4V10H20V14Z';

/**
 * Checkbox component
 */
const _Checkbox = (
    props: CheckboxProps,
    ref: ForwardedRef<HTMLLabelElement>,
): JSX.Element => {
    const {
        id,
        children,
        value,
        defaultValue,
        onChange = () => null,
        indeterminate = false,
        disabled = false,
        valid = true,
        inputProps,
        ...otherProps
    } = props;

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

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

    return (
        <StyledLabel
            ref={ref}
            data-disabled={disabled || undefined}
            {...otherProps}
        >
            <StyledInput
                id={id}
                type="checkbox"
                checked={internalValue}
                disabled={disabled}
                onFocus={() => {
                    // show the focus
                    setFocused(true);
                }}
                onBlur={() => {
                    // hide the focus
                    setFocused(false);
                }}
                onChange={(event) => {
                    if (disabled) {
                        return;
                    }

                    setInternalValue(event.target.checked);
                }}
                aria-checked={internalValue}
                {...inputProps}
            />
            <StyledCheckbox
                checked={internalValue}
                valid={valid}
                data-disabled={disabled || undefined}
                data-focused={focused || undefined}
                aria-hidden={true}
            >
                {internalValue && indeterminate && (
                    <Icon path={mdiMinusThick} size="1rem" />
                )}
                {internalValue && !indeterminate && (
                    <Icon path={mdiCheckBold} size="1rem" />
                )}
                {!internalValue && !indeterminate && <>&nbsp;</>}
            </StyledCheckbox>
            <StyledText>{children}</StyledText>
        </StyledLabel>
    );
};

export const Checkbox = forwardRef(_Checkbox) as (
    props: CheckboxProps & {
        ref?: ForwardedRef<HTMLLabelElement>;
    },
) => ReturnType<typeof _Checkbox>;
