import type { FC } from 'react';
import React, { Fragment, createContext, useContext, useState, useCallback } from 'react';
import type { ExpandableProps, ExpandableContextValue } from './types';

const ExpandableContext = createContext<ExpandableContextValue | undefined>(undefined);

/**
 * Hook to access the Expandable context
 * @throws {Error} When used outside of an Expandable component
 */
export const useExpandable = (): ExpandableContextValue => {
    const context = useContext(ExpandableContext);
    if (!context) throw 'useExpandable must be used within an Expandable component';

    return context;
};

/**
 * Expandable component that provides context for collapsible content.
 * Use with Expandable.Trigger and Expandable.Body sub-components.
 */
export const Expandable: FC<ExpandableProps> = ({
    children,
    defaultExpanded = true,
    expanded: controlledExpanded,
    onExpanded,
    isExpandable = true,
    as,
    testId,
    ...rest
}) => {
    const [expanded, setExpanded] = useState(defaultExpanded);

    const isExpanded = controlledExpanded !== undefined ? controlledExpanded : expanded;

    const handleSetExpanded = useCallback(
        (newExpanded: boolean) => {
            setExpanded(newExpanded);
            onExpanded?.(newExpanded);
        },
        [onExpanded]
    );

    const contextValue: ExpandableContextValue = {
        isExpanded,
        setExpanded: handleSetExpanded,
        isExpandable,
    };

    const ChildrenWrapperType = as || Fragment;

    return (
        <ExpandableContext.Provider value={contextValue}>
            {ChildrenWrapperType === Fragment ? (
                children
            ) : (
                <ChildrenWrapperType data-testid={testId} {...rest}>
                    {children}
                </ChildrenWrapperType>
            )}
        </ExpandableContext.Provider>
    );
};
