import React, { useEffect, useState, forwardRef } from 'react';

import { styled, theme, keyframes } from '../../stitches.config';
import { Scroll } from '../Scroll';

const rotate = keyframes({
    '0%': {
        transform: 'rotate(0deg) scale(0.8)',
    },
    '50%': {
        transform: 'rotate(360deg) scale(1.2)',
    },
    '100%': {
        transform: 'rotate(720deg) scale(0.8)',
    },
});

const ball1 = keyframes({
    '0%': {
        boxShadow: `30px 0 0 ${theme.colors['primary-1']}`,
        transform: 'translate(-15px, -10px)',
    },
    '50%': {
        boxShadow: `0 0 0 ${theme.colors['primary-1']}`,
        transform: 'translate(0px, 10px)',
    },
    '100%': {
        boxShadow: `30px 0 0 ${theme.colors['primary-1']}`,
        transform: 'translate(-15px, -10px)',
    },
});

const ball2 = keyframes({
    '0%': {
        boxShadow: `30px 0 0 ${theme.colors['primary-1']}`,
        transform: 'translate(-15px, 10px)',
    },
    '50%': {
        boxShadow: `0 0 0 ${theme.colors['primary-1']}`,
        transform: 'translate(0px, -10px)',
    },
    '100%': {
        boxShadow: `30px 0 0 ${theme.colors['primary-1']}`,
        transform: 'translate(-15px, 10px)',
    },
});

const StyledContainer = styled('div', {
    position: 'absolute',
    inset: '0',
    zIndex: '60',
    overflow: 'hidden',
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    gap: theme.space['2'],
    textAlign: 'center',
    backgroundColor: theme.colors['transparent-light-2'],
    backdropFilter: 'blur(1px)',
});

const StyledSection = styled('div', {
    display: 'flex',
    flexDirection: 'column',
    height: '50%',
    variants: {
        type: {
            top: {
                justifyContent: 'end',
            },
            bottom: {
                justifyContent: 'start',
                padding: theme.space['2'],
                gap: theme.space['2'],
            },
        },
    },
});

const StyledSpinnerHolder = styled('div', {
    display: 'flex',
});

const StyledSpinner = styled('div', {
    animation: `${rotate} 1s infinite`,
    '&::before': {
        borderRadius: '50%',
        content: '',
        display: 'block',
        height: '20px',
        width: '20px',
        animation: `${ball1} 1s infinite`,
        backgroundColor: theme.colors['primary-1'],
    },
    '&::after': {
        borderRadius: '50%',
        content: '',
        display: 'block',
        height: '20px',
        width: '20px',
        animation: `${ball2} 1s infinite`,
        backgroundColor: theme.colors['primary-1'],
    },
});

const StyledMessage = styled('div', {
    color: theme.colors['grey-1'],
    fontWeight: theme.fontWeights.bold,
    fontSize: theme.fontSizes.default,
    textTransform: 'uppercase',
});

const StyledDescription = styled(Scroll, {
    flex: '1',
    color: theme.colors['grey-2'],
    fontSize: theme.fontSizes.sm,
});

export interface LoadingProps extends React.ComponentPropsWithRef<'div'> {
    /** Mark the loading in a open/closed state */
    open: boolean;
    /** Time till the loading is open */
    delay?: number;
    /** Message to render in the Loading */
    message?: React.ReactNode;
    /** Description to render in the Loading */
    description?: React.ReactNode;
}

/**
 * Loading component
 */
export const Loading = forwardRef<HTMLDivElement, LoadingProps>(
    (props, ref): JSX.Element => {
        const {
            open = false,
            delay = 0,
            message = 'Loading',
            description,
            ...otherProps
        } = props;

        const [visible, setVisible] = useState<boolean>(open);

        useEffect(() => {
            // close it if it isn't open
            if (!open) {
                setVisible(false);
                return;
            }

            if (delay === 0) {
                setVisible(true);
                return;
            }

            // delay the visibility if open
            const timer = setTimeout(() => {
                setVisible(true);
            }, delay);

            return () => clearTimeout(timer);
        }, [open, delay]);

        // don't show anything if not visible
        if (!visible) {
            return <></>;
        }

        return (
            <StyledContainer ref={ref} {...otherProps}>
                <StyledSection type={'top'}>
                    <StyledSpinnerHolder>
                        <StyledSpinner />
                    </StyledSpinnerHolder>
                </StyledSection>
                <StyledSection type={'bottom'}>
                    <StyledMessage>{message}</StyledMessage>
                    {description && (
                        <StyledDescription>{description}</StyledDescription>
                    )}
                </StyledSection>
            </StyledContainer>
        );
    },
);

Loading.displayName = 'Loading';
