import * as React from 'react';
import { useRef } from 'react';
import join from '../utils/join';
import useProperty from '../utils/useProperty';
import { Box, Flex } from '../Flex';
import { twMerge } from '../../twMerge';
import clsx from 'clsx';
const TabContext = React.createContext({
    selectedIndex: 0,
    tabCount: 0,
    setSelectedIndex: (index) => { },
});
const isTab = (c) => {
    return c.type === Tab || SupportedTabMenuComps.has(c.type);
};
const isTabContent = (c) => {
    return c.type === Content || SupportedTabContentComps.has(c.type);
};
export const Tabs = (props) => {
    const { autoFocus, children, onValueChange: _onValueChange, defaultValue: _defaultValue, value: _value, keyboardNavigation = true, selectedIndex: _selectedIndex, defaultSelectedIndex: _defaultSelectedIndex, onSelectedIndexChange: _onSelectedIndexChange, ...boxProps } = props;
    let [selectedIndex, doSetSelectedIndex] = useProperty(props, 'selectedIndex', 0);
    const [selectedValue, setSelectedValue] = useProperty(props, 'value', 0);
    const allChildren = React.Children.toArray(children);
    const values = [];
    const tabs = allChildren.filter(isTab).map((tab, index) => {
        values.push(tab.props.value);
        return React.cloneElement(tab, {
            // @ts-ignore ignore
            index,
            keyboardNavigation,
            autoFocus,
        });
    });
    const contents = allChildren.filter(isTabContent);
    const contentValues = contents.map((c) => c.props.value);
    const shouldUseValue = (props.value !== undefined || props.defaultValue !== undefined) &&
        props.selectedIndex === undefined &&
        props.defaultSelectedIndex === undefined;
    if (shouldUseValue) {
        const indexOfValue = values.indexOf(selectedValue);
        if (indexOfValue > -1) {
            selectedIndex = indexOfValue;
        }
        const reorderContents = contentValues.filter((contentValue) => values.indexOf(contentValue) != -1).length ===
            tabs.length;
        if (reorderContents) {
            contents.sort((c1, c2) => {
                const v1 = c1.props.value;
                const v2 = c2.props.value;
                return values.indexOf(v1) - values.indexOf(v2);
            });
        }
    }
    const setSelectedIndex = (index) => {
        if (shouldUseValue) {
            const selectedValue = values[index];
            setSelectedValue(selectedValue);
        }
        doSetSelectedIndex(index);
    };
    const currentContent = contents[selectedIndex];
    const { value, ...contentProps } = (currentContent?.props || {});
    const fillerClassName = clsx('ab-Tabs__Filler', 'twa:bg-(--ab-cmp-tabs-strip__background)');
    return (React.createElement(TabContext.Provider, { value: {
            selectedIndex,
            tabCount: tabs.length,
            setSelectedIndex,
        } },
        React.createElement(Box, { ...boxProps, className: join(boxProps.className, 'ab-Tabs twa:flex twa:flex-col'), "data-selected-index": selectedIndex },
            React.createElement(Flex, { flexDirection: "row", className: clsx('ab-Tabs__Strip', 'twa:bg-(--ab-cmp-tabs-strip__background)') },
                React.createElement(Box, { className: fillerClassName }),
                tabs,
                React.createElement(Flex, { className: clsx(fillerClassName, 'twa:flex-1') })),
            React.createElement(Box, { ...contentProps, className: `ab-Tabs__Body twa:p-2 twa:flex-1 ${contentProps.className || ''} ` }, currentContent))));
};
export const Tab = (props) => {
    const { index, autoFocus, keyboardNavigation = true, className: propsClassName, ...boxProps } = props;
    const context = React.useContext(TabContext);
    const baseClassName = 'ab-Tabs__Tab';
    const active = context.selectedIndex === props.index;
    const beforeActive = context.selectedIndex === props.index + 1;
    const className = clsx(baseClassName, active ? `${baseClassName}--active` : '', beforeActive ? `${baseClassName}--before-active` : '', 'twa:cursor-pointer twa:p-2', propsClassName);
    const ref = useRef(null);
    React.useEffect(() => {
        if (active && autoFocus && keyboardNavigation) {
            ref.current?.focus();
        }
    }, [active, autoFocus]);
    const mountedRef = React.useRef(false);
    React.useEffect(() => {
        // don't focus initially,
        // so we don't steal focus, if autoFocus is false
        if (!mountedRef.current) {
            return;
        }
        if (active && keyboardNavigation) {
            ref.current?.focus();
        }
    }, [active, keyboardNavigation]);
    React.useEffect(() => {
        mountedRef.current = true;
    }, []);
    return (React.createElement(Box, { ref: ref, tabIndex: active ? 0 : -1, ...boxProps, className: twMerge('twa:p-2', className), onClick: (event) => {
            context.setSelectedIndex(props.index);
            props.onClick?.(event);
        }, onKeyDown: (event) => {
            if (keyboardNavigation) {
                if (event.key === 'Enter') {
                    context.setSelectedIndex(props.index);
                }
                if (event.key === 'ArrowLeft') {
                    event.preventDefault();
                    let nextIndex = props.index - 1;
                    if (nextIndex < 0) {
                        nextIndex = context.tabCount - 1;
                    }
                    context.setSelectedIndex(nextIndex);
                }
                if (event.key === 'ArrowRight') {
                    event.preventDefault();
                    let nextIndex = props.index + 1;
                    if (nextIndex > context.tabCount - 1) {
                        nextIndex = 0;
                    }
                    context.setSelectedIndex(nextIndex);
                }
            }
            props.onKeyDown?.(event);
        } }));
};
export const SupportedTabMenuComps = new Set();
SupportedTabMenuComps.add(Tab);
export const Content = (props) => {
    return React.createElement(React.Fragment, null, props.children);
};
export const SupportedTabContentComps = new Set();
SupportedTabContentComps.add(Content);
Tabs.Tab = Tab;
Tabs.Content = Content;
