import React, { forwardRef, useMemo } from 'react';

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

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

import { StepperStep } from './StepperStep';

const StyledStepper = styled('ul', {
    display: 'flex',
    variants: {
        layout: {
            vertical: {
                flexDirection: 'column',
            },
            horizontal: {},
        },
    },
});

export interface StepperProps
    extends Omit<
        React.ComponentPropsWithRef<'ul'>,
        'value' | 'defaultValue' | 'onChange'
    > {
    /** Value of the stepper */
    value?: string;

    /** Default Value of the stepper */
    defaultValue?: string;

    /** Callback that is triggered when the step changes */
    onChange?: (value: string) => void;

    /** Can the steps be interacted with */
    interactive?: boolean;

    /** Layout of the steps */
    layout?: 'horizontal' | 'vertical';
}

/**
 * Stepper component
 */
export const Stepper = forwardRef<HTMLUListElement, StepperProps>(
    (props, ref): JSX.Element => {
        const {
            children,
            value,
            defaultValue,
            onChange = () => null,
            interactive = true,
            layout = 'vertical',
            ...otherProps
        } = props;

        const [internalValue, setInternalValue] = useValue<string>({
            initialValue: '',
            value: value,
            defaultValue: defaultValue,
            onChange: onChange,
        });

        // get the count of the valid children
        const count = React.Children.toArray(children).filter(
            (c) => React.isValidElement(c) && c.type === StepperStep,
        ).length;

        // calculate the stepIdx of the active step
        const stepIdx = useMemo(() => {
            let stepIdx = -1;
            React.Children.forEach(children, (c, index) => {
                if (React.isValidElement(c) && c.type === StepperStep) {
                    if (c.props.value === internalValue) {
                        stepIdx = index;
                    }
                }
            });

            return stepIdx;
        }, [children, internalValue]);

        /**
         * Render the children adding in additional properties
         * @returns cloned children elements
         */
        const render = () => {
            let remaining = count;

            return React.Children.map(children, (c, index) => {
                if (React.isValidElement(c) && c.type === StepperStep) {
                    // remove one
                    remaining--;

                    // pass in the hidden props
                    return React.cloneElement(c, {
                        key: `step--${index}`,
                        number: count - remaining,
                        highlighted: index < stepIdx,
                        selected: index === stepIdx,
                        last: remaining === 0,
                        interactive: interactive,
                        layout: layout,
                        onChange: (s: string) => {
                            setInternalValue(s);
                        },
                    } as React.Attributes);
                }

                return c;
            });
        };

        return (
            <StyledStepper ref={ref} layout={layout} {...otherProps}>
                {render()}
            </StyledStepper>
        );
    },
);

Stepper.displayName = 'Stepper';
