define('pullRequest/branchDeletion', [
    'aui',
    'jquery',
    'lodash',
    'bitbucket/util/navbuilder',
    'bitbucket/internal/model/page-state',
    'bitbucket/internal/util/ajax',
    'bitbucket/internal/util/client-storage',
    'bitbucket/internal/util/events',
    'exports'
], function (
    AJS,
    $,
    _,
    nav,
    pageState,
    ajax,
    clientStorage,
    events,
    exports
) {

    var dialogSelector = "#pull-request-merge-dialog";
    var deleteBranchSelector = '#delete-branch-checkbox';

    function getRestDeleteBranchUrl(dryRun) {
        return nav.newBuilder().addPathComponents('rest', 'pull-request-source-branch-deletion', '1.0',
            'projects', pageState.getProject().getKey(), 'repos', pageState.getRepository().getSlug(),
            'pull-requests', pageState.getPullRequest().getId()).withParams({ dryRun : !!dryRun }).build();
    }

    function showFailureDialog(errorMessage, next) {
        var dialog = new AJS.Dialog({
            width : 650,
            height : 200,
            id: 'branch-deletion-failure',
            closeOnOutsideClick: false
        });

        dialog.addHeader(AJS.I18n.getText('bitbucket.web.pullrequest.merge.dialog.delete-branch.failed.header'))
            .addPanel('', bitbucket.internal.plugin.pullRequestDeleteBranch.failureDialogContents({ errorMessage : errorMessage }))
            .addCancel(AJS.I18n.getText('bitbucket.web.button.close'), function() {
                dialog.remove();
                next();
            });

        dialog.show();
        dialog.updateHeight();
    }

    function findDominantError(errors) {
        // TODO this may need to be smarter than this
        if (errors && errors.errors && errors.errors.length) {

            return errors.errors[0].message;
        }
        return null;
    }

    function returnFalse() {
        return false;
    }

    function decorateMergePromise(ajaxMergePromise, removeDialog) {
        // we'll pass back this deferred - which will trigger the actions that would normally be done after the ajax merge call completes
        var deferred = $.Deferred();

        ajaxMergePromise.done(function () {
            var mergePromiseArgs = Array.prototype.slice.call(arguments);

            function resolveOuterPromise() {
                deferred.resolve.apply(this, mergePromiseArgs);
            }

            if ($(deleteBranchSelector).is(':checked')) {
                ajax.rest({
                    url: exports._getRestDeleteBranchUrl(),
                    type: 'POST',
                    statusCode : {
                        '400' : returnFalse,
                        '401' : returnFalse,
                        '403' : returnFalse,
                        '409' : returnFalse
                    }
                })
                .done(function() {
                    events.trigger('bitbucket.internal.page.pullRequest.revisionRefRemoved', null, pageState.getPullRequest().getFromRef().toJSON());
                    return resolveOuterPromise();
                })
                .fail(function(xhr, textStatus, errorThrown, errors) {
                    // We want the user to be warned of the branch deletion failure
                    // but continue on as usual as the merge succeeded
                    removeDialog();
                    var error = findDominantError(errors);
                    if (error) {
                        showFailureDialog(error, deferred.resolve);
                    } else {
                        resolveOuterPromise();
                    }
                });
            } else {
                resolveOuterPromise();
            }
        }).fail(function () {
            // merge failed - do whatever the original promise would do
            deferred.reject.apply(this, Array.prototype.slice.call(arguments));
        });

        return deferred.promise();
    }

    exports.initialiseBranchDeletion = function() {
        _.defer(function(){ //Wait until the soy template is inserted in the DOM
            var $deleteBranchCheckbox = $(dialogSelector).find(deleteBranchSelector);
            var deleteBranchKey = clientStorage.buildKey(['pr', 'merge', 'delete', 'branch'], 'repo');
            var checked = !!clientStorage.getItem(deleteBranchKey);

            $deleteBranchCheckbox.prop('checked', checked).on('change', function(){
                clientStorage.setItem(deleteBranchKey, !!this.checked, {
                    noCleanup: true
                });
            });

            var mergeDialog = AJS.dialog2(dialogSelector);
            mergeDialog.on('show', function checkBranchDeletionOnce() {
                checkBranchDeletion();
                mergeDialog.off('show', checkBranchDeletionOnce);
            });
        });
    };

    function checkBranchDeletion() {
        var $deleteBranchCheckbox = $(dialogSelector).find(deleteBranchSelector);

        // Check if the source branch can be deleted, and if not, disable the checkbox or if yes, enable the checkbox
        ajax.rest({
            url: getRestDeleteBranchUrl(true),
            type: 'POST',
            dataType: 'json',
            statusCode : {
                '400' : returnFalse,
                '403' : returnFalse,
                '409' : returnFalse
            }
        }).done(function() {
            $deleteBranchCheckbox.removeAttr('disabled');
        }).fail(function(xhr, textStatus, errorThrown, errors) {
            var error = findDominantError(errors);
            if (error) {
                $deleteBranchCheckbox
                    .attr('disabled', 'disabled')
                    .removeAttr('checked')
                    .parent().tooltip({
                        delayOut: 200,
                        delayIn: 0,
                        gravity: 'n',
                        html: false,
                        hoverable: true,
                        offset: 7,
                        title: function () {
                            return error;
                        }
                    });
            }
        });
    }

    exports.getMergePromiseDecorator = decorateMergePromise;
    // To be mocked in testing only, so we don't have to mock the pageState in full
    exports._getRestDeleteBranchUrl = getRestDeleteBranchUrl;
});

// we need to manually require this module because we're modifying an existing page that doesn't require us
require('pullRequest/branchDeletion');
