import React from 'react';
import _ from 'lodash';
import ClassNames from 'classnames';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import domUtils from '../../utils/domUtils';
import Icon from '../../dataDisplay/icon';

class AccordionItem extends React.Component {
    constructor(props) {
        super(props);

        this.state = { isSelected: props.isSelected };

        this._onAnimationComplete = this._onAnimationComplete.bind(this);
        this._onClick = this._onClick.bind(this);
    }

    componentDidUpdate(prevProps) {
        if (this.props.isSelected !== prevProps.isSelected) {
            this.setState({ isSelected: this.props.isSelected });
        }
    }

    render() {
        const {
            children, className, style, subAccordion, summary,
        } = this.props;
        const { isSelected } = this.state;
        const containerClasses = ClassNames('accordion-item', className, {
            'accordion-item-is-active': isSelected,
            'accordion-item-sub-accordion': subAccordion,
            'accordion-item-no-summary': summary === false,
        });

        return (
            <div className={containerClasses} onClick={this._onClick} style={style}>
                {children}
            </div>
        );
    }

    _animationProps(el) {
        let a;
        const animations = {
            animation: 'animationend',
            OAnimation: 'oAnimationEnd',
            MozAnimation: 'animationend',
            WebkitAnimation: 'webkitAnimationEnd',
        };

        for (a in animations) {
            if (el.style[a] !== undefined) {
                return animations[a];
            }
        }
    }

    _onAnimationComplete() {
        const itemElement = ReactDOM.findDOMNode(this);
        const animationEvent = this._animationProps(itemElement);

        itemElement.removeEventListener(animationEvent, this._onAnimationComplete);

        const { scrollContainer, scrollContainerMarginHeight } = this.props;
        const scrollContainerEl = _.isString(scrollContainer) ? ReactDOM.findDOMNode(document.querySelector(scrollContainer)) : _.isObject(scrollContainer) ? scrollContainer : null;
        const containerHeight = scrollContainerEl ? scrollContainerEl.offsetHeight - (scrollContainerMarginHeight || 0) : window.innerHeight;
        const scrollPosition = domUtils.scrollPos(scrollContainerEl);
        const containerBottom = containerHeight + scrollPosition;
        const itemElHeight = itemElement.offsetHeight;
        const itemElTopYPosition = itemElement.offsetTop;
        const itemElBottomYPosition = itemElTopYPosition + itemElHeight;
        const belowFold = itemElBottomYPosition - (containerHeight + scrollPosition);

        if (containerBottom < itemElBottomYPosition) {
            domUtils.scrollTo(scrollPosition + belowFold, null, scrollContainerEl);
        }
    }

    _onClick() {
        const { isSelected } = this.state;

        if (!isSelected) {
            const itemElement = ReactDOM.findDOMNode(this);
            const animationEvent = this._animationProps(itemElement);

            itemElement.addEventListener(animationEvent, this._onAnimationComplete);
        }
    }
}

AccordionItem.propTypes = {
    className: PropTypes.string,
    isSelected: PropTypes.bool,
    scrollContainer: PropTypes.oneOfType([
        PropTypes.shape({}),
        PropTypes.string,
    ]),
    scrollContainerMarginHeight: PropTypes.number,
    style: PropTypes.shape({}),
    subAccordion: PropTypes.bool,
    summary: PropTypes.bool,
};

class AccordionSummary extends React.Component {
    constructor() {
        super();

        this._onClick = this._onClick.bind(this);
    }

    render() {
        return (
            <div className="accordion-item-summary" onClick={this._onClick}>
                {this.props.children}
            </div>
        );
    }

    _onClick() {
        this.props.onClick();
    }
}

AccordionSummary.propTypes = {
    onClick: PropTypes.func,
};

class AccordionCheckbox extends React.Component {
    render() {
        return (
            <div
                className="accordion-checkbox"
                style={{
                    flex: '0 1 auto',
                    minWidth: '44px',
                }}
            >
                {this.props.children}
            </div>
        );
    }
}

AccordionCheckbox.propTypes = {
    className: PropTypes.string,
    style: PropTypes.shape({}),
};

class AccordionContent extends React.Component {
    render() {
        return (
            <div className="accordion-item-content" style={this.props.style}>
                {this.props.children}
            </div>
        );
    }
}

AccordionContent.propTypes = {
    style: PropTypes.shape({}),
};

class Accordion extends React.Component {
    constructor(props) {
        super(props);

        let newSelected;

        if (props.exclusive === false) {
            newSelected = props.selected || [];
        } else {
            newSelected = _.isNil(props.selected) ? -1 : props.selected;
        }

        this.state = { selected: newSelected };
    }

    componentDidUpdate(prevProps, prevState) {
        const { selected: prevSelectedProp } = prevProps;
        const { exclusive, selected: nextSelectedProp } = this.props;

        if (!_.isEqual(prevSelectedProp, nextSelectedProp)) {
            let nextSelectedState;

            if (exclusive === false) {
                nextSelectedState = nextSelectedProp || [];
            } else {
                nextSelectedState = _.isNil(nextSelectedProp) ?
                    -1 : nextSelectedProp;
            }

            this.setState({ selected: nextSelectedState });
        }
    }

    render() {
        const {
            basic,
            children,
            className,
            exclusive,
            id,
            inverse,
            scrollContainer,
            scrollContainerMarginHeight,
            style,
        } = this.props;
        const { selected } = this.state;
        const containerClasses = ClassNames('ui', 'accordion', className, {
            'accordion-basic': basic,
            'accordion-inverse': inverse,
            'accordion-inclusive': exclusive === false,
        });
        const convertedChildren = _.isArray(children) ? children : [children];
        const items = _.map(convertedChildren, (child, index) => {
            const {
                children, onClick, style, subTitle, subAccordion, summary, title,
            } = child.props;
            const isSelected = exclusive === false ? _.includes(selected, index) : selected === index;

            if (title) {
                return (
                    <AccordionItem
                        isSelected={isSelected}
                        key={`accordion-item-${index}`}
                        scrollContainer={scrollContainer}
                        scrollContainerMarginHeight={scrollContainerMarginHeight}
                        style={style}
                        subAccordion={subAccordion}
                        summary={summary}
                    >
                        <div
                            className="accordion-item-title"
                            onClick={this._onSummaryClick.bind(this, index, onClick)}
                        >
                            <span className="copy">
                                {title}
                                {subTitle ? <span className="padding-left">{subTitle}</span> : null}
                            </span>
                            <Icon
                                compact
                                inverse={inverse}
                                rotate={isSelected ? 135 : 0}
                                title={isSelected ? 'Collapse' : 'Expand'}
                                type="plus"
                            />
                        </div>

                        <AccordionContent>
                            {children}
                        </AccordionContent>
                    </AccordionItem>
                );
            } if (summary === false) {
                return (
                    <AccordionItem
                        isSelected={isSelected}
                        key={`accordion-item-${index}`}
                        scrollContainer={scrollContainer}
                        scrollContainerMarginHeight={scrollContainerMarginHeight}
                        style={style}
                        subAccordion={subAccordion}
                        summary={summary}
                    >
                        <AccordionContent>
                            {children}
                        </AccordionContent>
                    </AccordionItem>
                );
            }
            return (
                <AccordionItem
                    isSelected={isSelected}
                    key={`accordion-item-${index}`}
                    scrollContainer={scrollContainer}
                    scrollContainerMarginHeight={scrollContainerMarginHeight}
                    style={style}
                    subAccordion={subAccordion}
                    summary={summary}
                >
                    {React.Children.map(children, (c, i) => {
                        if (i === 0) {
                            return (
                                <AccordionSummary onClick={this._onSummaryClick.bind(this, index, c.props.onClick)}>
                                    {c.props.children}
                                </AccordionSummary>
                            );
                        }
                        return c;
                    })}
                </AccordionItem>
            );
        });

        return (
            <div
                className={containerClasses}
                id={id}
                style={style}
            >
                {items}
            </div>
        );
    }

    _onSummaryClick(index, onClick) {
        const { exclusive } = this.props;
        const { selected } = this.state;
        let newSelected;

        if (exclusive === false) {
            const isSelected = _.includes(selected, index);

            newSelected = isSelected ? _.pull(selected, index) : _.union(selected, [index]);
        } else {
            newSelected = selected === index ? -1 : index;
        }

        if (_.isFunction(onClick)) {
            onClick(newSelected);
        }

        this.setState({ selected: newSelected });
    }
}

Accordion.Content = AccordionContent;
Accordion.Checkbox = AccordionCheckbox;
Accordion.Item = AccordionItem;
Accordion.Summary = AccordionSummary;

Accordion.propTypes = {
    basic: PropTypes.bool,
    className: PropTypes.string,
    exclusive: PropTypes.bool,
    id: PropTypes.string,
    inverse: PropTypes.bool,
    scrollContainer: PropTypes.oneOfType([
        PropTypes.shape({}),
        PropTypes.string,
    ]),
    scrollContainerMarginHeight: PropTypes.number,
    selected: PropTypes.oneOfType([
        PropTypes.array,
        PropTypes.number,
    ]),
    style: PropTypes.shape({}),
};

export default Accordion;
