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

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

import { BaseFormInputProps } from '../internal';

const buildButton = (
    color: NonNullable<ButtonProps['color']>,
    variant: NonNullable<ButtonProps['variant']>,
) => {
    let css = {};

    if (variant === 'filled') {
        css = {
            ...css,
            backgroundColor: `$${color}-1`,
            borderColor: `$${color}-1`,
            color: 'white',
            '&:hover': {
                backgroundColor: `$${color}-2`,
                borderColor: `$${color}-2`,
            },
            '&:disabled': {
                backgroundColor: `$grey-4`,
                borderColor: `$grey-4`,
                color: 'white',
                cursor: 'default',
                pointerEvents: 'none',
            },
        };
    } else if (variant === 'outline') {
        css = {
            ...css,
            backgroundColor: 'transparent',
            borderColor: `$${color}-1`,
            color: `$${color}-1`,
            '&:hover': {
                backgroundColor: `$${color}-5`,
                borderColor: `$${color}-2`,
            },
            '&:disabled': {
                backgroundColor: 'transparent',
                borderColor: `$grey-4`,
                color: `$grey-4`,
                cursor: 'default',
                pointerEvents: 'none',
            },
        };
    } else if (variant === 'text') {
        css = {
            ...css,
            backgroundColor: 'transparent',
            borderColor: `transparent`,
            color: `$${color}-1`,
            '&:hover': {
                backgroundColor: `$${color}-5`,
            },
            '&:disabled': {
                backgroundColor: 'transparent',
                borderColor: `transparent`,
                color: `$grey-4`,
                cursor: 'default',
                pointerEvents: 'none',
            },
        };
    }

    return {
        color: color,
        variant: variant,
        css: css,
    };
};

const StyledButton = styled('button', {
    alignItems: 'center',
    borderWidth: '$default',
    borderRadius: '$default',
    cursor: 'pointer',
    display: 'inline-flex',
    fontSize: '$sm',
    fontWeight: '$semibold',
    lineHeight: '$none',
    justifyContent: 'center',
    margin: '0',
    outline: 'none',
    textAlign: 'center',
    userSelect: 'none',
    whiteSpace: 'nowrap',
    '&:focus': {
        outline: '2px solid $primary-1',
        outlineOffset: '2px',
    },
    variants: {
        color: {
            primary: {},
            success: {},
            error: {},
            warning: {},
            grey: {},
        },
        variant: {
            filled: {},
            outline: {},
            text: {},
        },
        size: {
            sm: {
                padding: '0 $2',
                height: '$space$6',
            },
            md: {
                padding: '$1 $4',
                height: '$space$8',
            },
            lg: {
                padding: '$2 $6',
                height: '$space$10',
            },
        },
        block: {
            true: {
                display: 'flex',
                width: '$space$full',
                whiteSpace: 'nowrap',
                overflow: 'hidden',
                textOverflow: 'ellipsis',
            },
        },
    },
    compoundVariants: [
        buildButton('primary', 'filled'),
        buildButton('primary', 'outline'),
        buildButton('primary', 'text'),
        buildButton('success', 'filled'),
        buildButton('success', 'outline'),
        buildButton('success', 'text'),
        buildButton('error', 'filled'),
        buildButton('error', 'outline'),
        buildButton('error', 'text'),
        buildButton('warning', 'filled'),
        buildButton('warning', 'outline'),
        buildButton('warning', 'text'),
        buildButton('grey', 'filled'),
        buildButton('grey', 'outline'),
        buildButton('grey', 'text'),
    ],
    defaultVariants: {
        color: 'primary',
        variant: 'filled',
        size: 'md',
        block: false,
    },
});

const StyledAdditional = styled('div', {
    alignItems: 'center',
    fontSize: '$space$4',
    display: 'inline-flex',
    lineHeight: '$none',
    variants: {
        type: {
            prepend: {
                marginRight: '$space$2',
            },
            append: {
                marginLeft: '$space$2',
            },
        },
    },
});

export interface ButtonProps extends ComponentPropsWithRef<'button'> {
    /** Rendered color of the Button */
    color?: 'primary' | 'success' | 'error' | 'warning' | 'grey';

    /** Rendered size of the Button */
    size?: BaseFormInputProps<never>['size'];

    /** Set to true to take up the full space */
    block?: boolean;

    /** Rendered variant of the Button */
    variant?: 'filled' | 'outline' | 'text';

    /** Type of the button. Defaults to 'button' */
    type?: 'reset' | 'submit' | 'button';

    /** Content to add before the children */
    prepend?: React.ReactNode;

    /** Content to add after the children */
    append?: React.ReactNode;
}

/**
 * Button component
 */
const _Button = (
    props: ButtonProps,
    ref: ForwardedRef<HTMLButtonElement>,
): JSX.Element => {
    const {
        children,
        tabIndex = 0,
        disabled = false,
        type = 'button',
        prepend,
        append,
        ...otherProps
    } = props;

    return (
        <StyledButton
            ref={ref}
            tabIndex={tabIndex}
            disabled={disabled}
            type={type}
            {...otherProps}
        >
            {prepend && (
                <StyledAdditional type={'prepend'}>{prepend}</StyledAdditional>
            )}
            {children}
            {append && (
                <StyledAdditional type={'append'}>{append}</StyledAdditional>
            )}
        </StyledButton>
    );
};

export const Button = forwardRef(_Button) as (
    props: ButtonProps & {
        ref?: ForwardedRef<HTMLButtonElement>;
    },
) => ReturnType<typeof _Button>;
