import React, { FC, memo }  from 'react';
import { cn }               from '../../util/bem';
import { useEventCallback } from '../../util/event-callback';
import { ChildrenOf }       from '../../util/types';
import { ButtonPropsType }  from '../button/button.component';
import { TabsItem }         from './tabs-item.component';
import { TabsScrollButton } from './tabs-scroll-button.component';
import './tabs.component.scss';

export type TabsPropsType = {
    children: ChildrenOf<typeof TabsItem> | ChildrenOf<typeof TabsItem>[];
    onChange?: (e: React.MouseEvent, value: string) => void;
    orientation?: 'horizontal' | 'vertical';
    viewType?: 'radio-button' | 'tabs' | 'vertical-tabs';
    /**
     * `true` will show scroll buttons on sides in case of overflow
     * `false` will hide scroll buttons
     */
    hasScrollButtons?: boolean;
    stretch?: boolean;
    /**
     * Id of currently selected `Tab`, to deselect all tabs  use `false`
     */
    activeTab?: string | boolean;
};

const className = cn('tabs');

type Ref = HTMLDivElement;

export const Tabs: FC<TabsPropsType> = memo(React.forwardRef<Ref, TabsPropsType>((props, ref) => {
    const tabsRef = React.useRef<HTMLDivElement>(null);
    const tabsPanelContainerRef = React.useRef<HTMLDivElement>(null);

    const [ translateValue, setTranslateValue ] = React.useState(0);
    const [ displayScrollButtons, setDisplayScrollButtons ] = React.useState({ start: false, end: false });

    const scroll = (scrollValue: number) => {
        if (tabsPanelContainerRef.current) {
            const normalizeScroll = scrollValue > 0 ? scrollValue : 0;
            tabsPanelContainerRef.current.style.transform = (props.orientation === 'vertical')
                ? `translateY(-${ normalizeScroll }px)`
                : `translateX(-${ normalizeScroll }px)`;
        }
    };

    // scroll < > helper
    const moveTabsScroll = (direction: 'next' | 'back', orientation: TabsPropsType['orientation']) => {
        if (!tabsRef.current) {
            return;
        }
        const multiplier = direction === 'next' ? 1 : -1;

        const newTranslateValue = translateValue + tabsRef.current[ orientation === 'vertical' ? 'clientHeight' : 'clientWidth' ] * multiplier;

        if (tabsPanelContainerRef.current
            && newTranslateValue < tabsPanelContainerRef.current[ orientation === 'vertical' ? 'scrollHeight' : 'scrollWidth' ]) {
            setTranslateValue(newTranslateValue);
        }
    };

    // scroll <--
    const handleStartScrollClick = () => { moveTabsScroll('back', props.orientation); };

    // scroll -->
    const handleEndScrollClick = () => { moveTabsScroll('next', props.orientation); };

    const getScrollButtons = () => {
        const scrollButtonsActive = displayScrollButtons.start || displayScrollButtons.end;
        const showScrollButtons =  props.hasScrollButtons && scrollButtonsActive;

        return {
            buttonStart: showScrollButtons ? (
                <TabsScrollButton
                    direction={ props.orientation === 'horizontal' ? 'left' : 'top' }
                    onClick={ handleStartScrollClick }
                    visible={ displayScrollButtons.start }
                />
            ) : null,
            buttonEnd: showScrollButtons ? (
                <TabsScrollButton
                    direction={ props.orientation === 'horizontal' ? 'right' : 'bottom' }
                    onClick={ handleEndScrollClick }
                    visible={ displayScrollButtons.end }
                />
            ) : null
        };
    };

    const getScrollParams = (tabPanelContainer: HTMLDivElement, tabs: HTMLDivElement) => (
        props.orientation === 'vertical'
            ? {
                showStartScroll: translateValue > 0 && translateValue - tabs.clientHeight < tabPanelContainer.scrollHeight,
                showEndScroll:   translateValue + tabs.clientHeight < tabPanelContainer.scrollHeight
            }
            : {
                showStartScroll: translateValue > 0 && translateValue - tabs.clientWidth < tabPanelContainer.scrollWidth,
                showEndScroll:   translateValue + tabs.clientWidth < tabPanelContainer.scrollWidth
            });

    // check if any scroll buttons needed, because content is partly invisible
    const isScrollButtonsVisible = useEventCallback(() => {
        if (props.hasScrollButtons && tabsPanelContainerRef.current && tabsRef.current) {
            const { showStartScroll: start, showEndScroll: end } = getScrollParams(tabsPanelContainerRef.current, tabsRef.current);

            if (start !== displayScrollButtons.start || end !== displayScrollButtons.end) {
                setDisplayScrollButtons({ start, end });
            }
        }
    });

    React.useEffect(() => {
        isScrollButtonsVisible();
    }, [ translateValue, props.children ]);

    React.useLayoutEffect(() => {
        scroll(translateValue);
    }, [ translateValue ]);

    const scrollButtons = getScrollButtons();

    const getItemStyle = (child: any) => {
        let computedStyles: ButtonPropsType = {};
        if (props.orientation === 'horizontal') {
            computedStyles = { ...{ viewType: 'secondary', align: 'center' } };
        } else {
            // computedStyles = { ...{ viewType: 'secondary', align: 'center' } };
            // @ts-ignore
            computedStyles = { ...{ viewType: 'transparent', align: 'left' } };
        }
        if (props.viewType === 'radio-button') {
            computedStyles = { ...{ size: 'xs', flex: 1 } };
            if (child.props.tabId === props.activeTab) {
                computedStyles = { ...{ viewType: 'primary' } };
            }
        }
        return computedStyles;
    };


    return (
        <div
            className={ className({ orientation: props.orientation, viewType: props.viewType }) }
            ref={ ref }
        >
            { scrollButtons.buttonStart }
            <div
                className={ className('scroller') }
                ref={ tabsRef }
            >
                <div
                    className={ className('container') }
                    ref={ tabsPanelContainerRef }
                    role="tablist"
                >
                    {
                        React.Children.map(props.children, (child) => (
                            React.isValidElement(child)
                                ? React.cloneElement(child, {
                                    // @ts-ignore
                                    selected: child.props.tabId === props.activeTab,
                                    // @ts-ignore
                                    tabId:    child.props.tabId,
                                    onChange: props.onChange,
                                    viewType: props.orientation === 'horizontal' ? 'secondary' : 'transparent',
                                    align:    props.orientation === 'horizontal' ? 'center' : 'left',
                                    ...getItemStyle(child)
                                })
                                : null))
                    }
                </div>
            </div>
            { scrollButtons.buttonEnd }
        </div>
    );
}));

Tabs.defaultProps = { orientation: 'horizontal', viewType: 'tabs' };
