/**
 * Drop-down list component
 *
 * @author Brauer Ilya
 * @date 28-02-2020
 */

import * as React from 'react';
import {PLACEMENT, Popover, TRIGGER} from '../../index';
import {Header} from './Header';
import {Item} from './Item';
import {Splitter} from './Splitter';
import {IProps as PopoverProps} from '../popover/Popover.types';
import * as styles from './dropdown.m.scss';

type IProps = PopoverProps & {
    isDisabled?: boolean;
    'data-qaid'?: string;
    forwardRef?: React.RefObject<Popover>;
    hasNavigation?: boolean;
}

interface IState {
    navigateIndex: number | undefined;
    childrenCount: number;
}

export class DropDown extends React.Component<IProps, IState> {
    static defaultProps = {
        trigger: TRIGGER.CLICK,
        placement: PLACEMENT.BOTTOM_START,
        hasShadow: true,
        hasArrow: true,
        hasPaddings: false,
        mouseEnterDelay: 10,
        mouseLeaveDelay: 10
    };

    static Header = Header;
    static Item = Item;
    static Splitter = Splitter;

    override state: IState = {
        navigateIndex: undefined,
        childrenCount: React.Children.count(this.props.children)
    }

    navigateIndexes: number[] = [];

    override componentDidMount () {
        if (this.props.children && Boolean(this.props.hasNavigation)) {
            this.initNavigation();
        }
    }

    override componentDidUpdate (prevProps: Readonly<IProps>, prevState: Readonly<IState>, snapshot?: any) {
        /*
         * For correct dropdown navigation in case, if data doesn't available at first render, need to pass null.
         */
        if (Boolean(this.props.hasNavigation) && prevProps.isOpen === false &&
            this.props.isOpen === true && this.navigateIndexes.length > 0
        ) {
            this.setState({navigateIndex: undefined});
        }

        const childrenCount = React.Children.count(this.props.children);

        if (Boolean(this.props.hasNavigation) && (childrenCount !== this.state.childrenCount)) {
            this.initNavigation();
            this.setState({childrenCount});
        }

        if (prevProps.children && !this.props.children) {
            document.removeEventListener('keydown', this.onKeyDown);
        }
    }

    override componentWillUnmount () {
        document.removeEventListener('keydown', this.onKeyDown);
    }

    initNavigation = () => {
        const navigateIndexes: number[] = [];

        React.Children.forEach(this.props.children, (child, index) => {
            if (child && typeof child === 'object' && 'type' in child && child.type === DropDown.Item) {
                navigateIndexes.push(index);
            }
        });

        this.navigateIndexes = navigateIndexes;
        document.addEventListener('keydown', this.onKeyDown);
    }

    onKeyDown = (e: KeyboardEvent): void => {
        if (this.props.isOpen && Boolean(this.props.hasNavigation)) {
            if (e.key === 'ArrowUp') {
                this.setState((prevState) => {
                    if (prevState.navigateIndex !== undefined) {
                        return {
                            navigateIndex: Math.max(prevState.navigateIndex - 1, 0),
                            childrenCount: prevState.childrenCount
                        };
                    }

                    return prevState;
                });
            } else if (e.key === 'ArrowDown') {
                this.setState((prevState) => {
                    if (prevState.navigateIndex !== undefined) {
                        return {
                            navigateIndex: Math.min(prevState.navigateIndex + 1, this.navigateIndexes.length - 1),
                            childrenCount: prevState.childrenCount
                        };
                    } else if (this.navigateIndexes.length > 0) {
                        return {
                            navigateIndex: 0,
                            childrenCount: prevState.childrenCount
                        };
                    }

                    return prevState;
                });
            }
        }
    }

    override render () {
        const {isDisabled, isOpen, onVisibleChange, forwardRef, hasNavigation, ...otherProps} = this.props;
        const navigateIndex = this.state.navigateIndex;

        return (
            <Popover
                {...otherProps}
                ref={forwardRef}
                isOpen={isDisabled === true ? false : isOpen}
                onVisibleChange={isDisabled === true ? undefined : onVisibleChange}
            >
                <div data-qaid={this.props['data-qaid']} data-component-id={'DropDown'} className={styles.ddContent}>
                    {
                        navigateIndex === undefined || !hasNavigation ? (
                            this.props.children
                        ) : (
                            React.Children.map(this.props.children, (child,  index) => {
                                if (index === this.navigateIndexes[navigateIndex] && child &&
                                    typeof child === 'object' && 'type' in child && child.type === DropDown.Item
                                ) {
                                    const childClone = React.cloneElement(child, {...child.props, isSelected: true});

                                    return childClone;
                                }

                                return child;
                            })
                        )
                    }
                </div>
            </Popover>
        );
    }
}
