define('bitbucket/internal/feature/branch-creation/form', [
    'aui',
    'jquery',
    'lodash',
    'bitbucket/util/navbuilder',
    'bitbucket/internal/feature/repository/branch-diagram',
    'bitbucket/internal/feature/repository/global-repository-selector',
    'bitbucket/internal/feature/repository/revision-reference-selector',
    'bitbucket/internal/model/repository',
    'bitbucket/internal/model/revision-reference',
    'bitbucket/internal/util/ajax',
    'bitbucket/internal/util/dom-event',
    'bitbucket/internal/util/events',
    'bitbucket/internal/util/navigator',
    'bitbucket/internal/widget/simple-select',
    'bitbucket/internal/widget/submit-spinner'
], function (
    AJS,
    $,
    _,
    nav,
    BranchDiagram,
    GlobalRepositorySelector,
    RevisionReferenceSelector,
    Repository,
    RevisionReference,
    ajax,
    domEvent,
    events,
    navigatorUtil,
    SimpleSelect,
    SubmitSpinner
) {

    var CUSTOM_BRANCH_TYPE = {
        id: 'CUSTOM',
        displayName: 'Custom',
        prefix: ''
    };

    function getCreatedBranchCheckoutUrl(repository, revisionRef) {
        return nav.project(repository.getProject())
            .repo(repository)
            .withParams({
                'at': revisionRef.id
            }).build();
    }

    function getCreateBranchRestUrl(repository) {
        return nav.rest('branch-utils')
            .project(repository.getProject().getKey())
            .repo(repository.getSlug())
            .addPathComponents('branches').build();
    }

    function getBranchModelRestUrl(repository) {
        return nav.rest('branch-utils')
            .project(repository.getProject().getKey())
            .repo(repository.getSlug())
            .addPathComponents('branchmodel').build();
    }

    function BranchCreationForm($formEl, recentRepositories, initialBranchTypes) {
        _.bindAll(this, 'createBranch', '_noBranchTypes');

        this.$form = $formEl;
        this.repositorySelector = new GlobalRepositorySelector(this.$form.find('#repository-selector'), {
            id: 'repository-selector-dialog',
            permission: 'REPO_WRITE',
            preloadData: GlobalRepositorySelector.constructDataPageFromPreloadArray(recentRepositories)
        });
        this.$repositorySpinner = this.$form.find('.repository-spinner');
        this.repository = this.repositorySelector.getSelectedItem();
        this.branchFromSelector = new RevisionReferenceSelector(this.$form.find('.branch-from-selector'), {
            id: 'branch-from-selector-dialog',
            repository: this.repository
        });
        this.branchDiagram = new BranchDiagram(this.$form.find('.branch-diagram'));
        this.branchType = null; // init select later
        this.$branchName = this.$form.find('.branch-name');
        this.$submitButton = this.$form.find('#create-branch-submit');
        this.$cancelButton = this.$form.find('#cancel');
        this.submitSpinner = new SubmitSpinner(this.$submitButton, 'before');

        this.init(initialBranchTypes);
    }

    BranchCreationForm.prototype.init = function(initialBranchTypes) {
        var self = this;

        this.branchType = new SimpleSelect('#branch-type', '#branch-type-menu', {
            onSelect: function(prefix) {
                self._updatePrefix(prefix);
                self._updateDiagramTarget();
                self.branchFromSelector.$trigger.focus(); // focus next input
            }
        });

        events.on('bitbucket.internal.feature.repository.repositorySelector.repositoryChanged', function(repository){
            if (this === self.repositorySelector) {
                self.setRepository(repository);
            }
        });

        events.on('bitbucket.internal.feature.repository.revisionReferenceSelector.revisionRefChanged', function(revisionReference) {
            if(this === self.branchFromSelector){
                self.branchDiagram.updateSourceRef(revisionReference);
            }
            self._setDisabledSubmitButton();
            self.$branchName.focus(); // focus next input
        });

        this.$branchName.on('keyup paste change cut drop', _.debounce(
            function() {
                self._updateDiagramTarget();
                self._setDisabledSubmitButton();
            }, 200)
        );

        this.$form.on('submit', domEvent.preventDefault(function() {
            if (!self.$submitButton.prop('disabled')) {
                // form can still be submitted via 'enter' even though submit button has been disabled so we should check
                self.createBranch();
            }
        }));
        this.$form.on('keydown', function(e) {
            if ((navigatorUtil.isMac() ? e.metaKey : e.ctrlKey) && e.which === AJS.keyCode.ENTER) {
                e.preventDefault();
                self.$form.trigger('submit');
            }
        });

        this.$cancelButton.on('click', domEvent.preventDefault(window.history.back.bind(window.history)));

        this.branchTypesDialog = AJS.dialog2(aui.dialog.dialog2({
            titleText: AJS.I18n.getText('bitbucket.branchmodel.web.branchtypes'),
            content: bitbucket.internal.feature.branchmodel.info.branchTypeList({
                branchTypes: initialBranchTypes
            })
        }));

        $('#branch-type-dialog-trigger').on('click', function(e) {
            e.preventDefault();
            self.branchTypesDialog.show();
        });
    };

    BranchCreationForm.prototype.createBranch = function() {
        var self = this;
        this.submitSpinner.show();
        this._setDisabledForm(true);

        ajax.rest({
            url: getCreateBranchRestUrl(self.repository),
            type: 'POST',
            data: {
                name: this.getBranchName(),
                startPoint: this.getBranchFrom().getId()
            },
            statusCode : {
                '400' : false,
                '403' : false,
                '404' : false,
                '409' : false,
                '500' : false // all handled by the .fail handler
            }
        }).done(function(revisionRef){
            events.trigger('bitbucket.internal.feature.branch-creation.branchCreated', revisionRef);
            window.location = getCreatedBranchCheckoutUrl(self.repository, revisionRef);
        }).fail(function(xhr, textStatus, errorThrown, data) {
            if (data && data.errors) {
                self.$form.siblings('.aui-message').remove();
                self.$form.find('.error').remove();

                var contextualErrors = _.filter(data.errors, function(error) {
                    return !!error.context;
                });
                _.each(contextualErrors, function(error) {
                    if (error.context === 'name') {
                        self.$branchName.after(widget.aui.form.fieldError({message: error.message}));
                    } else if (error.context === 'startPoint') {
                        self.branchFromSelector.$trigger.after(widget.aui.form.fieldError({message: error.message}));
                    }
                });

                var globalErrors = _.filter(data.errors, function(error) {
                    return !error.context;
                });
                var errorBody = _.reduce(globalErrors, function(body, error) {
                    return body + bitbucket.internal.widget.errorContent(error);
                }, "");
                if (errorBody.length > 0) {
                    require(['aui/flag'], function(flag) {
                        flag({
                            type: 'error',
                            body: errorBody
                        });
                    });
                }
            }

            self.submitSpinner.hide();
            self._setDisabledForm(false);
        });
    };

    BranchCreationForm.prototype.setRepository = function(repository){
        if (!repository || !repository instanceof Repository) {
            return;
        }
        this.repositorySelector.$trigger.closest('.field-group').find('.error').remove(); // Firstly, remove any repository field errors

        this.repository = repository;
        this.branchFromSelector.setRepository(this.repository);
        // Setting the repository empties out the currently selected branch,
        // but it doesn't fire the .revisionRefChanged event, so the diagram doesn't get updated.
        // Manually clear out the sourceRef from the diagram
        this.branchDiagram.updateSourceRef();
        this._updateBranchTypes();
    };

    BranchCreationForm.prototype.getBranchFrom = function(){
        return this.branchFromSelector.getSelectedItem();
    };

    BranchCreationForm.prototype.getBranchName = function(){
        //If branch-name-prefix exists in the DOM, this will use it's value, otherwise .text() returns an empty string
        //Returns an empty string if there is no user entered value (even if there is a prefix)
        return this.$branchName.val() ? this.$branchName.prev('.branch-name-prefix').text() + this._slugifyBranchName(this.$branchName.val()) : '';
    };

    BranchCreationForm.prototype._slugifyBranchName = function(branchName) {
        return branchName.replace(/\s+/g, '-');
    };

    BranchCreationForm.prototype._updateBranchTypes = function() {
        var self = this;
        this._setDisabledForm(true);
        this.$repositorySpinner.spin();
        ajax.rest({
            url: getBranchModelRestUrl(this.repository),
            type: 'GET',
            statusCode: {
                404: function() {
                    // No branch model for this repository
                    self._setDisabledForm(false);
                    self._updatePrefix();
                    self._updateDiagramTarget();
                    self.branchFromSelector.$trigger.focus(); // focus next input
                    return false;
                },
                409: function(xhr, textStatus, errorThrown, resp) {
                    var isEmptyRepo = _.find(resp.errors, function(error) { return error.exceptionName === "com.atlassian.bitbucket.repository.EmptyRepositoryException"; });
                    if (isEmptyRepo) {
                        self.repositorySelector.$trigger.closest('.field-group').append(widget.aui.form.fieldError({
                            message: AJS.I18n.getText('bitbucket.page.branch.creation.error.repository.empty')
                        }));
                        self.repositorySelector.$trigger.prop('disabled', false).focus(); // focus same input
                        return false;
                    }
                }
            }
        }).done(function(data) {
            self._setDisabledForm(false);
            var branchTypes = data.types;

            if (branchTypes && branchTypes.length) {
                branchTypes.push(CUSTOM_BRANCH_TYPE);

                self.branchType.updateList(bitbucket.internal.feature.branchCreation.branchTypesList({
                    branchTypes: branchTypes,
                    selectedBranchTypeId: self.branchType.getSelectedId()   // Try to restore previous branchType
                }));

                self._updatePrefix(self.branchType.getSelectedValue());
                self._updateDiagramTarget();

                self.branchType.$trigger.closest('.field-group').removeClass('hidden');
                self.branchType.$trigger.focus(); // focus next input

                self.branchTypesDialog.$el.find('.aui-dialog2-content').html(
                    bitbucket.internal.feature.branchmodel.info.branchTypeList({
                        branchTypes: branchTypes
                    })
                );
            } else {
                self._noBranchTypes();
            }
        }).fail(function() {
            self._noBranchTypes();
        }).always(function() {
            self.$repositorySpinner.spinStop();
        });
    };

    BranchCreationForm.prototype._noBranchTypes = function() {
        this.$branchName.prev('.branch-name-prefix').remove();
        this.branchType.$trigger.closest('.field-group').addClass('hidden');
    };

    BranchCreationForm.prototype._updatePrefix = function(prefix) {
        this.$branchName.prev('.branch-name-prefix').remove();
        if (prefix && prefix.length) {
            var newPrefix = bitbucket.internal.feature.branchCreation.branchNamePrefix({ prefix: prefix });
            this.$branchName.before(newPrefix);
        }
    };

    BranchCreationForm.prototype._updateDiagramTarget = function(){
        var branchName = this.getBranchName();
        var targetRef = new RevisionReference({
            'id' : branchName,
            'displayId' : branchName,
            'type' : RevisionReference.type.BRANCH
        });

        this.branchDiagram.updateTargetRef(targetRef);
    };

    BranchCreationForm.prototype._setDisabledSubmitButton = function(disable) {
        var branchFrom = this.getBranchFrom();
        disable = disable || !(this.getBranchName() && branchFrom && branchFrom.getId());
        this.$submitButton.prop('disabled', disable);
    };

    BranchCreationForm.prototype._setDisabledForm = function(disable) {
        this._setDisabledSubmitButton(disable);
        // jQuery takes an array of elements but not an array of jquery objects :(
        $([
            this.repositorySelector.$trigger[0],
            this.branchFromSelector.$trigger[0],
            this.branchType.$trigger[0],
            this.$branchName[0]
        ]).prop('disabled', disable);
    };

    return BranchCreationForm;
});
