import $ from 'jquery';
import _ from 'lodash';
import Widget from '../widget/widget';

export default class ListAndDetailView extends Widget {

    /**
     * @callback selectionCallback
     * @param {jQuery} $listItem - the element that was invoked
     * @param {jQuery} $listView - the control's list view
     * @param {jQuery} $detailView - the control's detail view
     * @param {Event} e - the click event object
     */

    /**
     * @param {HTMLElement|jQuery} el - The element to use as a container for this ListAndDetailView
     * @param {object} options - options for this ListAndDetailView
     * @param {selectionCallback?} options.selectHandler - invoked when an item in the list is clicked
     * @param {string?} options.selectedClass - CSS class name that is applied to selected list items
     * @param {boolean?} options.selectFirstOnInit - select the first item in the list on init
     * @param {string?} options.detailContent - HTML string that is injected into the detail/content view
     * @param {string?} options.listContent - HTML string that is is injected into the list view
     */
    constructor(el, options) {
        super(options);

        this.keycodes = {
            j: 74,
            k: 75,
        };

        this.$el = $(el);
        this.$el.html(bitbucket.internal.component.listAndDetailView.main({
            listContent: options.listContent || '',
            detailContent: options.detailContent || '',
        }));
        this.$listView = this.$el.find('.list-view');
        this.$detailView = this.$el.find('.detail-view');
        this.selectHandler = options.selectHandler;

        this.bindShortcuts();
        this.$listView.on('click', this.options.listItemTag, this._itemClickHandler);
        this._maybeSelectFirst();
    }

    destroy() {
        super.destroy();
        this.unbindShortcuts();
        this.$el.empty();
    }

    _itemClickHandler(e) {
        const selectedClass = this.options.selectedClass;
        this.$listView.find(`li.${selectedClass}`).removeClass(selectedClass);
        const $listItem = $(e.currentTarget).addClass(selectedClass);
        this.selectHandler($listItem, this.$listView, this.$detailView, e);
    }

    _maybeSelectFirst() {
        if (this.options.selectFirstOnInit) {
            this.$listView.find(`${this.options.listItemTag}:first`).click();
        }
    }

    _shortcutHandler(e) {
        const $selected = this.$listView.find(`${this.options.listItemTag}.${this.options.selectedClass}`);
        let $target;
        if (e.which === this.keycodes.j) {
            $target = $selected.next(this.options.listItemTag);
        } else if (e.which === this.keycodes.k) {
            $target = $selected.prev(this.options.listItemTag);
        } else {
            return;
        }

        $target.click()
            .find('a').focus().blur();   // Scroll into view if necessary. Will only scroll if there is an <a>
    }

    bindShortcuts() {
        $(document).on('keydown', this._shortcutHandler);
    }

    unbindShortcuts() {
        $(document).off('keydown', this._shortcutHandler);
    }

    /**
     * Transitions and removes the specified item from the listView.
     *
     * @param {jQuery} $item  - item to remove
     * @param {string} selector - jQuery selector of next/prev item to click
     * @returns {Promise} a jQuery promise that completes when the item has been removed
     */
    removeItem($item, selector) {
        const deferred = $.Deferred();

        function _removeItem() {
            $item.remove();
            deferred.resolve();
        }

        $item.addClass('offScreen').one('transitionend', _removeItem);

        if ($item.next(selector).length) {
            $item.next(selector).click();
        } else if ($item.prev(selector).length) {
            $item.prev(selector).click();
        } else {
            // No more items in the list.
            this.$detailView.empty();
        }
        return deferred.promise();
    }

    /**
     * Transitions and adds the specified item to the listView.
     *
     * @param {jQuery} $item - to be added
     * @param {jQuery?} $beforeElement - element before which to insert the specified $item.
     *                 If not supplied, the element will be appended to $listView.
     * @param {boolean?} select - true, if the added item should be selected, false otherwise
     */
    addItem($item, $beforeElement, select) {
        $item.addClass('offScreen');
        if ($beforeElement) {
            $item.insertBefore($beforeElement);
        } else {
            $item.appendTo(this.$listView);
        }
        $item.offset(); // Ensure the browser paints the item before the transition.
        $item.removeClass('offScreen');
        if (select) {
            $item.click();
        }
    }
}

ListAndDetailView.defaults = {
    selectedClass: 'selected-item',
    selectFirstOnInit: false,
    listItemTag: 'li',
    selectHandler: _.noop,
};
