import $ from 'jquery';
import _ from 'lodash';
import React, { Component, PropTypes } from 'react';

import AsyncSelect from './components/async-select';
import Filter from './components/filter';
import Select from './components/select';
import Toggle from './components/toggle';

/**
 * Calls React.cloneElement, but for any props that already contains callbacks, combines the callbacks and ensures both the old version
 * and new version are called.
 *
 * If you've got a better name, and I'm sure you do, please rename this.
 *
 * @param {ReactElement} el - el to clone
 * @param {Object} newProps - properties to override
 * @returns {ReactElement}
 */
function cloneSequencedOverwrite(el, newProps) {
    const combinedProps = _.clone(newProps, function(newVal, prop) {
        const elVal = el[prop];
        if (!_.isFunction(elVal) || !_.isFunction(newVal)) {
            return newVal;
        }
        return function() {
            elVal.apply(el, arguments);
            newVal.apply(el, arguments);
        };
    });
    return React.cloneElement(el, combinedProps);
}

export default class FilterBar extends Component {

    static get propTypes() {
        return {
            children: PropTypes.any,
            onChange: PropTypes.func,
            id: PropTypes.string,
        };
    }

    constructor(...args) {
        super(...args);

        this.filterRefs = [];
    }

    getState() {
        const state = {};
        this.filterRefs.forEach(filter => {
            if (!filter) {
                return;
            }
            state[filter.props.id] = filter.domValue();
        });
        return state;
    }

    reset() {
        const oldState = this.getState();
        const resetPromises = this.filterRefs.map(filter => {
            if (!filter) {
                return $.Deferred().resolve();
            }
            return filter.reset();
        });
        $.when(...resetPromises).then(() => {
            const newState = this.getState();
            if (Object.keys(newState).some(prop => newState[prop] !== oldState[prop])) {
                this.props.onChange(newState);
            }
        });
    }

    render() {
        let onChange;
        if (this.props.onChange) {
            onChange = () => {
                this.props.onChange(this.getState());
            };
        }

        return (
            <div className="filter-bar" id={this.props.id}>
                <h6 className="filter-label">{AJS.I18n.getText('bitbucket.component.filter.bar.label')}</h6>
                <ul>
                    {React.Children.map(this.props.children, (c, i) => {
                        if (!c || !(c.type.prototype instanceof Filter)) {
                            return c;
                        }
                        return cloneSequencedOverwrite(c, {
                            onChange: onChange || undefined,
                            ref: r => (this.filterRefs[i] = r),
                        });
                    })}
                </ul>
            </div>
        );
    }
}

FilterBar.AsyncSelect = AsyncSelect;
FilterBar.Select = Select;
FilterBar.Toggle = Toggle;
