/**
 * Provides utilities for views to work with xblocks.
 */
define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_utils', 'js/utils/module',
        'js/models/xblock_info', 'edx-ui-toolkit/js/utils/string-utils'],
    function($, _, gettext, ViewUtils, ModuleUtils, XBlockInfo, StringUtils) {
        'use strict';
        var addXBlock, duplicateXBlock, deleteXBlock, createUpdateRequestData, updateXBlockField, VisibilityState,
            getXBlockVisibilityClass, getXBlockListTypeClass, updateXBlockFields, getXBlockType, findXBlockInfo,
            moveXBlock;

        /**
         * Represents the possible visibility states for an xblock:
         *
         *   live - the block and all of its descendants are live to students (excluding staff only)
         *     Note: Live means both published and released.
         *
         *   ready - the block is ready to go live and all of its descendants are live or ready (excluding staff only)
         *     Note: content is ready when it is published and scheduled with a release date in the future.
         *
         *   unscheduled - the block and all of its descendants have no release date (excluding staff only)
         *     Note: it is valid for items to be published with no release date in which case they are unscheduled.
         *
         *   needsAttention - the block or its descendants need attention
         *     i.e. there is some content that is not fully live, ready, unscheduled or staff only.
         *     For example: one subsection has draft content, or there's both unreleased and released content
         *     in one section.
         *
         *   staffOnly - all of the block's content is to be shown to staff only
         *     Note: staff only items do not affect their parent's state.
         */
        VisibilityState = {
            live: 'live',
            ready: 'ready',
            unscheduled: 'unscheduled',
            needsAttention: 'needs_attention',
            staffOnly: 'staff_only',
            gated: 'gated'
        };

        /**
         * Adds an xblock based upon the data attributes of the specified add button. A promise
         * is returned, and the new locator is passed to all done handlers.
         * @param target The add button that was clicked upon.
         * @returns {jQuery promise} A promise representing the addition of the xblock.
         */
        addXBlock = function(target) {
            var parentLocator = target.data('parent'),
                category = target.data('category'),
                displayName = target.data('default-name');
            return ViewUtils.runOperationShowingMessage(gettext('Adding'),
                function() {
                    var addOperation = $.Deferred();
                    analytics.track('Created a ' + category, {
                        'course': course_location_analytics,
                        'display_name': displayName
                    });
                    $.postJSON(ModuleUtils.getUpdateUrl(),
                        {
                            'parent_locator': parentLocator,
                            'category': category,
                            'display_name': displayName
                        }, function(data) {
                            var locator = data.locator;
                            addOperation.resolve(locator);
                        });
                    return addOperation.promise();
                });
        };

        /**
         * Duplicates the specified xblock element in its parent xblock.
         * @param {jquery Element}  xblockElement  The xblock element to be duplicated.
         * @param {jquery Element}  parentElement  Parent element of the xblock element to be duplicated,
         *      new duplicated xblock would be placed under this xblock.
         * @returns {jQuery promise} A promise representing the duplication of the xblock.
         */
        duplicateXBlock = function(xblockElement, parentElement) {
            return ViewUtils.runOperationShowingMessage(gettext('Duplicating'),
                function() {
                    var duplicationOperation = $.Deferred();
                    $.postJSON(ModuleUtils.getUpdateUrl(), {
                        duplicate_source_locator: xblockElement.data('locator'),
                        parent_locator: parentElement.data('locator')
                    }, function(data) {
                        duplicationOperation.resolve(data);
                    })
                    .fail(function() {
                        duplicationOperation.reject();
                    });
                    return duplicationOperation.promise();
                });
        };

        /**
         * Moves the specified xblock in a new parent xblock.
         * @param {String}  sourceLocator  Locator of xblock element to be moved.
         * @param {String}  targetParentLocator  Locator of the target parent xblock, moved xblock would be placed
         *      under this xblock.
         * @param {Integer}  targetIndex  Intended index position of the xblock in parent xblock. If provided,
         *      xblock would be placed at the particular index in the parent xblock.
         * @returns {jQuery promise} A promise representing the moving of the xblock.
         */
        moveXBlock = function(sourceLocator, targetParentLocator, targetIndex) {
            var moveOperation = $.Deferred(),
                operationText = targetIndex !== undefined ? gettext('Undo moving') : gettext('Moving');
            return ViewUtils.runOperationShowingMessage(operationText,
                function() {
                    $.patchJSON(ModuleUtils.getUpdateUrl(), {
                        move_source_locator: sourceLocator,
                        parent_locator: targetParentLocator,
                        target_index: targetIndex
                    }, function(response) {
                        moveOperation.resolve(response);
                    })
                    .fail(function() {
                        moveOperation.reject();
                    });
                    return moveOperation.promise();
                });
        };

        /**
         * Deletes the specified xblock.
         * @param xblockInfo The model for the xblock to be deleted.
         * @param xblockType A string representing the type of the xblock to be deleted.
         * @returns {jQuery promise} A promise representing the deletion of the xblock.
         */
        deleteXBlock = function(xblockInfo, xblockType) {
            var deletion = $.Deferred(),
                url = ModuleUtils.getUpdateUrl(xblockInfo.id),
                operation = function() {
                    ViewUtils.runOperationShowingMessage(gettext('Deleting'),
                        function() {
                            return $.ajax({
                                type: 'DELETE',
                                url: url
                            }).success(function() {
                                deletion.resolve();
                            });
                        }
                    );
                },
                messageBody;
            xblockType = xblockType || 'component'; // eslint-disable-line no-param-reassign
            messageBody = StringUtils.interpolate(
                    gettext('Deleting this {xblock_type} is permanent and cannot be undone.'),
                    {xblock_type: xblockType},
                    true
                );

            if (xblockInfo.get('is_prereq')) {
                messageBody += ' ' + gettext('Any content that has listed this content as a prerequisite will also have access limitations removed.');   // eslint-disable-line max-len
                ViewUtils.confirmThenRunOperation(
                    StringUtils.interpolate(
                        gettext('Delete this {xblock_type} (and prerequisite)?'),
                        {xblock_type: xblockType},
                        true
                    ),
                    messageBody,
                    StringUtils.interpolate(
                        gettext('Yes, delete this {xblock_type}'),
                        {xblock_type: xblockType},
                        true
                    ),
                    operation
                );
            } else {
                ViewUtils.confirmThenRunOperation(
                    StringUtils.interpolate(
                        gettext('Delete this {xblock_type}?'),
                        {xblock_type: xblockType},
                        true
                    ),
                    messageBody,
                    StringUtils.interpolate(
                        gettext('Yes, delete this {xblock_type}'),
                        {xblock_type: xblockType},
                        true
                    ),
                    operation
                );
            }
            return deletion.promise();
        };

        createUpdateRequestData = function(fieldName, newValue) {
            var metadata = {};
            metadata[fieldName] = newValue;
            return {
                metadata: metadata
            };
        };

        /**
         * Updates the specified field of an xblock to a new value.
         * @param {Backbone Model} xblockInfo The XBlockInfo model representing the xblock.
         * @param {String} fieldName The xblock field name to be updated.
         * @param {*} newValue The new value for the field.
         * @returns {jQuery promise} A promise representing the updating of the field.
         */
        updateXBlockField = function(xblockInfo, fieldName, newValue) {
            var requestData = createUpdateRequestData(fieldName, newValue);
            return ViewUtils.runOperationShowingMessage(gettext('Saving'),
                function() {
                    return xblockInfo.save(requestData, {patch: true});
                });
        };

        /**
         * Updates the specified fields of an xblock to a new values.
         * @param {Backbone Model} xblockInfo The XBlockInfo model representing the xblock.
         * @param {Object} xblockData Object representing xblock data as accepted on server.
         * @param {Object} [options] Hash with options.
         * @returns {jQuery promise} A promise representing the updating of the xblock values.
         */
        updateXBlockFields = function(xblockInfo, xblockData, options) {
            options = _.extend({}, {patch: true}, options);
            return ViewUtils.runOperationShowingMessage(gettext('Saving'),
                function() {
                    return xblockInfo.save(xblockData, options);
                }
            );
        };

        /**
         * Returns the CSS class to represent the specified xblock visibility state.
         */
        getXBlockVisibilityClass = function(visibilityState) {
            if (visibilityState === VisibilityState.staffOnly) {
                return 'is-staff-only';
            }
            if (visibilityState === VisibilityState.gated) {
                return 'is-gated';
            }
            if (visibilityState === VisibilityState.live) {
                return 'is-live';
            }
            if (visibilityState === VisibilityState.ready) {
                return 'is-ready';
            }
            if (visibilityState === VisibilityState.needsAttention) {
                return 'has-warnings';
            }
            return '';
        };

        getXBlockListTypeClass = function(xblockType) {
            var listType = 'list-unknown';
            if (xblockType === 'course') {
                listType = 'list-sections';
            } else if (xblockType === 'section') {
                listType = 'list-subsections';
            } else if (xblockType === 'subsection') {
                listType = 'list-units';
            }
            return listType;
        };

        getXBlockType = function(category, parentInfo, translate) {
            var xblockType = category;
            if (category === 'chapter') {
                xblockType = translate ? gettext('section') : 'section';
            } else if (category === 'sequential') {
                xblockType = translate ? gettext('subsection') : 'subsection';
            } else if (category === 'vertical' && (!parentInfo || parentInfo.get('category') === 'sequential')) {
                xblockType = translate ? gettext('unit') : 'unit';
            }
            return xblockType;
        };

        findXBlockInfo = function(xblockWrapperElement, defaultXBlockInfo) {
            var xblockInfo = defaultXBlockInfo,
                xblockElement,
                displayName,
                hasChildren;
            if (xblockWrapperElement.length > 0) {
                xblockElement = xblockWrapperElement.find('.xblock');
                displayName = xblockWrapperElement.find(
                    '.xblock-header .header-details .xblock-display-name'
                ).text().trim();
                // If not found, try looking for the old unit page style rendering.
                // Only used now by static pages.
                if (!displayName) {
                    displayName = xblockElement.find('.component-header').text().trim();
                }
                hasChildren = defaultXBlockInfo ? defaultXBlockInfo.get('has_children') : false;
                xblockInfo = new XBlockInfo({
                    id: xblockWrapperElement.data('locator'),
                    courseKey: xblockWrapperElement.data('course-key'),
                    category: xblockElement.data('block-type'),
                    display_name: displayName,
                    has_children: hasChildren
                });
            }
            return xblockInfo;
        };

        return {
            VisibilityState: VisibilityState,
            addXBlock: addXBlock,
            moveXBlock: moveXBlock,
            duplicateXBlock: duplicateXBlock,
            deleteXBlock: deleteXBlock,
            updateXBlockField: updateXBlockField,
            getXBlockVisibilityClass: getXBlockVisibilityClass,
            getXBlockListTypeClass: getXBlockListTypeClass,
            updateXBlockFields: updateXBlockFields,
            getXBlockType: getXBlockType,
            findXBlockInfo: findXBlockInfo
        };
    });
