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

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

import { Icon } from '../Icon';
import { IconButton, IconButtonProps } from '../IconButton';

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

    if (variant === 'filled') {
        css = {
            ...css,
            backgroundColor: `$${color}-5`,
            borderColor: `transparent`,
            borderWidth: '$default',
            borderRadius: '$default',
        };
    } else if (variant === 'outline') {
        css = {
            ...css,
            backgroundColor: `$base`,
            borderColor: `$${color}-1`,
            borderWidth: '$default',
            borderRadius: '$default',
        };
    } else if (variant === 'notification') {
        css = {
            ...css,
            backgroundColor: `$base`,
            borderColor: `$${color}-1`,
            borderLeftWidth: '$thicker',
            borderRadius: '$default',
            boxShadow: '$default',
        };
    }

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

const StyledAlert = styled('div', {
    alignItems: 'flex-start',
    color: '$grey-1',
    columnGap: '$2',
    display: 'flex',
    flexDirection: 'row',
    fontSize: '$sm',
    lineHeight: '1rem',
    outline: 'none',
    userSelect: 'none',
    width: '$space$full',
    variants: {
        color: {
            primary: {},
            success: {},
            error: {},
            warning: {},
        },
        variant: {
            filled: {},
            outline: {},
            notification: {},
        },
        size: {
            sm: {
                padding: '0 $2',
            },
            md: {
                padding: '$2 $4',
            },
            lg: {
                padding: '$2 $6',
            },
        },
    },
    compoundVariants: [
        buildAlert('primary', 'filled'),
        buildAlert('primary', 'outline'),
        buildAlert('primary', 'notification'),
        buildAlert('success', 'filled'),
        buildAlert('success', 'outline'),
        buildAlert('success', 'notification'),
        buildAlert('error', 'filled'),
        buildAlert('error', 'outline'),
        buildAlert('error', 'notification'),
        buildAlert('warning', 'filled'),
        buildAlert('warning', 'outline'),
        buildAlert('warning', 'notification'),
    ],
    defaultVariants: {
        color: 'primary',
        variant: 'filled',
        size: 'md',
    },
});

const StyledAlertIcon = styled(Icon, {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    height: '$space$6',
    width: '$space$6',
    variants: {
        color: {
            primary: {
                color: `$primary-1`,
            },
            success: {
                color: `$success-1`,
            },
            error: {
                color: `$error-1`,
            },
            warning: {
                color: `$warning-1`,
            },
        },
    },
    defaultVariants: {
        color: 'primary',
    },
});

const StyledAlertText = styled('div', {
    flex: '1 1 0',
    margin: '$1 0 0 0',
});

const StyledAlertAction = styled('div', {
    margin: '$1 0 0 0',
});

// TODO: Need to figure out why @mdi/js isn't tree shaking
const mdiInformation =
    'M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z';
const mdiCheckCircle =
    'M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M10 17L5 12L6.41 10.59L10 14.17L17.59 6.58L19 8L10 17Z';
const mdiAlert = 'M13 14H11V9H13M13 18H11V16H13M1 21H23L12 2L1 21Z';
const mdiAlertCircle =
    'M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z';
const mdiClose =
    'M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z';

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

    /** Rendered variant of the Alert */
    variant?: 'filled' | 'outline' | 'notification';

    /** Action to pass into the Alert */
    action?: ReactNode;

    /** Toggle to show the close icon */
    closeable?: boolean;

    /** Callback triggered on close */
    onClose?: () => void;

    /** Props to pass to the close */
    closeProps?: Omit<IconButtonProps, 'size'>;
}

/**
 * Alert component
 */
const _Alert = (
    props: AlertProps,
    ref: ForwardedRef<HTMLDivElement>,
): JSX.Element => {
    const {
        children,
        role = 'alert',
        variant = 'filled',
        color = 'primary',
        action,
        closeable = true,
        onClose = () => null,
        closeProps = {},
        ...otherProps
    } = props;

    return (
        <StyledAlert
            ref={ref}
            role={role}
            variant={variant}
            color={color}
            {...otherProps}
        >
            {color === 'primary' && (
                <StyledAlertIcon color={'primary'} path={mdiInformation} />
            )}
            {color === 'success' && (
                <StyledAlertIcon color={'success'} path={mdiCheckCircle} />
            )}
            {color === 'error' && (
                <StyledAlertIcon color={'error'} path={mdiAlert} />
            )}
            {color === 'warning' && (
                <StyledAlertIcon color={'warning'} path={mdiAlertCircle} />
            )}
            <StyledAlertText>{children}</StyledAlertText>
            {action && <StyledAlertAction>{action}</StyledAlertAction>}
            {closeable && (
                <IconButton onClick={onClose} {...closeProps}>
                    <Icon path={mdiClose} />
                </IconButton>
            )}
        </StyledAlert>
    );
};

export const Alert = forwardRef(_Alert) as (
    props: AlertProps & {
        ref?: ForwardedRef<HTMLDivElement>;
    },
) => ReturnType<typeof _Alert>;
