define('bitbucket/internal/v2/feature/pull-request/header/pull-request-header-view', [
    'aui',
    'aui/flag',
    'jquery',
    'lodash',
    'react',
    'react-redux',
    'bitbucket/internal/bbui/models/models',
    'bitbucket/internal/bbui/pull-request-header/pull-request-header',
    'bitbucket/internal/feature/pull-request-edit',
    'bitbucket/internal/model-transformer',
    'bitbucket/internal/model/page-state',
    'bitbucket/internal/model/participant',
    'bitbucket/internal/model/pull-request',
    'bitbucket/internal/util/ajax',
    'bitbucket/internal/util/events',
    'bitbucket/internal/util/shortcuts',
    'bitbucket/internal/widget/submit-spinner',
    '../action-creators/can-merge',
    '../action-creators/change-reviewer-status',
    '../action-creators/change-self-reviewer',
    '../action-creators/watch',
    './get-action-url',
    './merge-dialog',
    './set-dialog-buttons-disabled',
    './update-pull-request'
], function(
    AJS,
    auiFlag,
    $,
    _,
    React,
    ReactRedux,
    models,
    PullRequestHeader,
    PullRequestEdit,
    transformer,
    pageState,
    Participant,
    PullRequest,
    ajax,
    events,
    shortcuts,
    SubmitSpinner,
    canMerge,
    changeReviewerStatus,
    changeSelfReviewer,
    watch,
    getActionUrl,
    mergeDialog,
    setDialogButtonsDisabled,
    updatePullRequest
) {

    'use strict';

    var TRIGGERED_BY_KEYBOARD = { triggeredBy : 'keyboardShortcut' };
    var pullRequest;

    function initKeyboardFlags() {
        var shortcutFlag;

        function showApprovalUpdateFlag(options) {
            var flagTitle = options.approved ?
                AJS.I18n.getText('bitbucket.web.pullrequest.toolbar.approved.updateflag') :
                AJS.I18n.getText('bitbucket.web.pullrequest.toolbar.unapproved.updateflag');
            showUpdateFlag(flagTitle, options);
        }

        function showWatchUpdateFlag(options) {
            var flagTitle = options.watchState ?
                AJS.I18n.getText('bitbucket.web.watchable.watched.tooltip') :
                AJS.I18n.getText('bitbucket.web.watchable.unwatched.tooltip');
            showUpdateFlag(flagTitle, options);
        }

        function showUpdateFlag(flagTitle, options) {
            if (_.isMatch(options, TRIGGERED_BY_KEYBOARD)) {
                if (shortcutFlag) {
                    shortcutFlag.close();
                }

                shortcutFlag = auiFlag({
                    type: 'success',
                    title: flagTitle,
                    close: 'auto'
                });
            }
        }

        events.on('bitbucket.internal.widget.approve-button.added', showApprovalUpdateFlag);
        events.on('bitbucket.internal.widget.approve-button.removed', showApprovalUpdateFlag);
        events.on('bitbucket.internal.web.watch-button.added', showWatchUpdateFlag);
        events.on('bitbucket.internal.web.watch-button.removed', showWatchUpdateFlag);
    }





    function createDeclineDialog(options) {
        var declineDialog = AJS.dialog2(bitbucket.internal.feature.pullRequest.decline.dialog({'content':options.confirmDialog.content}));

        // we manually add the dialog to the body so that it's on the DOM and available for the branch deletion plugin
        // to disable the checkbox
        $('body').append(declineDialog.$el);

        var declineXhr;

        declineDialog.$el.find('.confirm-button').on('click', function() {

            var spinner = new SubmitSpinner(this, 'before');

            setDialogButtonsDisabled(declineDialog, true);
            spinner.show();

            declineXhr = ajax.rest($.extend({
                url: getActionUrl('decline'),
                type: 'POST'
            }, options.ajax));

            var declinePromise = declineXhr;

            declineXhr.fail(function(xhr, textStatus, errorThrown, resp) {
                if (xhr.status === 400) {
                    var $declineDialogContent = declineDialog.$el.find('.aui-dialog2-content');

                    if (resp.errors) {
                        $declineDialogContent.children('.aui-message').remove();
                        $declineDialogContent.prepend(bitbucket.internal.feature.pullRequest.decline.errors({'errors': resp.errors }));
                    }
                } else {
                    declineDialog.hide();
                }
            }).always(function() {
                spinner.hide();
                setDialogButtonsDisabled(declineDialog, false);
                declineXhr = null;
            });

            declinePromise.done(function(StashPullRequestJSON) {
                events.trigger('bitbucket.internal.feature.pullRequest.declined', null, {
                    user: pageState.getCurrentUser().toJSON(),
                    pullRequest: StashPullRequestJSON
                });
                options.callback(StashPullRequestJSON);
            });
        });

        declineDialog.$el.find('.cancel-button').on('click', function() {
            if (declineXhr) {
                declineXhr.abort();
                declineXhr = null;
            }
            declineDialog.hide();
        });

        return declineDialog;
    }

    var PullRequestHeaderView = React.createClass({

        propTypes: {
            hasRepoWrite: React.PropTypes.bool.isRequired,
            mergeTimeout: React.PropTypes.number
        },

        getInitialState: function() {
            return {};
        },

        componentDidMount: function () {
            var pullRequest = pageState.getPullRequest();
            // TODO destroy / re-init if Pull Request changes
            // use the pageState pullRequest for legacy PullRequestEdit, it expects a Brace model
            this._pullRequestEdit = new PullRequestEdit(pullRequest);

            var self = this;
            shortcuts.bind('pullRequestApprove', function() {
                var currentUserStatus = self.props.currentUserAsReviewer && self.props.currentUserAsReviewer.state;
                var newStatus = (currentUserStatus === models.ApprovalState.APPROVED) ?
                    models.ApprovalState.UNAPPROVED :
                    models.ApprovalState.APPROVED;
                self.onStatusClick(_.merge({newStatus: newStatus}, TRIGGERED_BY_KEYBOARD));
            });

            shortcuts.bind('pullRequestEdit', function() {
                if (pullRequest.getState() !== models.PullRequestState.MERGED) {
                    self._pullRequestEdit.show();
                }
            });

            $(document).on('click', '.add-description', function() {
                self._pullRequestEdit.show();
            });

            shortcuts.bind('pullRequestWatch', function(){
                self.toggleWatch(TRIGGERED_BY_KEYBOARD);
            });

            events.once('bitbucket.internal.feature.comments.commentAdded', function () {
                // When a user comments check if they were already a participant
                // if they weren't then they should be set to watch the PR
                var allParticipants = [pullRequest.getAuthor().toJSON()]
                    .concat(pullRequest.getReviewers().toJSON())
                    .concat(pullRequest.getParticipants().toJSON());
                var isParticipant = allParticipants.some(function(model) {
                    return model.user.name === pageState.getCurrentUser().id;
                });
                if (!isParticipant) {
                    pullRequest.setParticipants(pullRequest.getParticipants().toJSON()
                        .concat([new Participant({user: pageState.getCurrentUser()})]));
                    self.toggleWatch({watchState: true});
                }
            });

            this.mergeCheck();
            mergeDialog.initMergeDialog(this.props);
            this.initMergeEvents();

            this.initDeclineButton();
            initKeyboardFlags();
        },

        onMoreAction: function(action) {
            switch (action) {
                case 'edit':
                    this._pullRequestEdit.show();
                    break;
                case 'watch':
                    this.toggleWatch();
                    break;
                case 'decline':
                    this.declineDialog.show();
                    break;
            }
        },

        onReOpenClick: function() {
            var self = this;
            // pass along the promise so sub components can deal with promise outcomes too
            return ajax.rest({
                url: getActionUrl('reopen'),
                type: 'POST'
            }).done(function(StashPullRequestJSON) {
                events.trigger('bitbucket.internal.feature.pullRequest.reopened', null, {
                    user: pageState.getCurrentUser().toJSON(),
                    pullRequest: StashPullRequestJSON
                });

                updatePullRequest(transformer.pullRequest(StashPullRequestJSON), true, self.props);
                self.mergeCheck();
            });
        },

        onSelfClick: function (addOrRemoveSelf, unwatch) {
            this.props.dispatch(changeSelfReviewer(
                this.props.pullRequest,
                this.props.currentUser,
                addOrRemoveSelf,
                this.props.currentUserAsReviewer && this.props.currentUserAsReviewer.state
            ));

            if (addOrRemoveSelf === 'ADD_SELF') {
                this.props.dispatch(watch({
                    stateOnly: true,
                    watchState: true
                }));
            }

            if (unwatch) {
                this.props.dispatch(watch({ watchState: false }));
            }
        },

        onStatusClick: function (options) {
            if (!this.props.currentUserAsReviewer) {
                console.warn('Current user is not a reviewer');
            } else {
                this.props.dispatch(changeReviewerStatus(_.merge({
                    pullRequest: this.props.pullRequest,
                    user: this.props.currentUser,
                    oldStatus: this.props.currentUserAsReviewer.state
                }, options)));
            }
        },

        onMergeHelpDialogClose: function() {
            this.setState({ showMergeHelpDialog: false });
        },
        getConditions: function() {
            var isAuthor = this.props.pullRequest.author.user.name === this.props.currentUser.name;
            var canWrite = this.props.hasRepoWrite;
            var canEdit = canWrite || isAuthor;
            return {
                canMerge: canWrite,
                canDecline: canEdit,
                canEdit: canEdit,
                canReOpen: canEdit
            };
        },

        toggleWatch: function(options) {
            this.props.dispatch(watch(_.merge({}, { watchState: !this.props.pullRequest.isWatching }, options)));
        },

        mergeCheck: function() {
            if (this.props.pullRequest.state === models.PullRequestState.OPEN) {
                this.props.dispatch(canMerge(new PullRequest(this.props.pullRequest._stash)));
            }
        },

        initMergeEvents: function() {
            const self = this;
            events.on('bitbucket.internal.feature.pull-request.merge-check', this.mergeCheck);

            events.on('bitbucket.internal.branch.plugin.conflict.merge.help', function (mergeHelp) {
                self.setState({mergeHelp: mergeHelp});
            });

            events.on('bitbucket.internal.pull-request.show.cant.merge.help', function () {
                // an internal component wants to show the merge help - lets pass that along to the PR header
                self.setState({showMergeHelpDialog: true});
            });
        },

        initDeclineButton: function($declineButton) {
            var panelContent = "<p class='decline-message'>" +
                AJS.I18n.getText('bitbucket.web.pullrequest.decline.dialog.message') + "</p>";

            var self = this;
            var options = {
                buttonSelector : '.decline-pull-request',
                confirmDialog : {
                    content: panelContent
                },
                ajax : {
                    statusCode : {
                        '401' :  function(xhr, textStatus, errorThrown, errors, dominantError) {
                            return $.extend({}, dominantError, {
                                title: AJS.I18n.getText('bitbucket.web.pullrequest.decline.error.401.title'),
                                message: AJS.I18n.getText('bitbucket.web.pullrequest.decline.error.401.message'),
                                fallbackUrl: false,
                                shouldReload: true
                            });
                        }
                    }
                },
                callback: function(StashPullRequestJSON) {
                    updatePullRequest(transformer.pullRequest(StashPullRequestJSON), true, self.props);
                    self.declineDialog.hide();
                }
            };

            this.declineDialog = createDeclineDialog(options);
        },

        render: function() {
            var props = {
                pullRequest: this.props.pullRequest,
                conditions: this.getConditions(),
                currentUser: this.props.currentUser,
                currentUserAsReviewer: this.props.currentUserAsReviewer,
                currentUserIsWatching: this.props.pullRequest.isWatching,
                mergeHelp: this.state.mergeHelp,
                onMoreAction: this.onMoreAction,
                currentUserStatus: this.props.currentUserAsReviewer && this.props.currentUserAsReviewer.state,
                onMergeClick: mergeDialog.onMergeClick,
                onReOpenClick: this.onReOpenClick,
                onSelfClick: this.onSelfClick,
                onStatusClick: this.onStatusClick,
                permissionToReview: this.props.currentUser.name !== this.props.pullRequest.author.user.name,
                showMergeHelpDialog: this.state.showMergeHelpDialog,
                onMergeHelpDialogClose: this.onMergeHelpDialogClose
            };
            return <PullRequestHeader {...props} />;
        }
    });

    function mapStateToProps(state) {

        function currentUserAsReviewer() {
            return _.find(state.pullRequest.reviewers, function(reviewer) {
                return reviewer.user.name === state.currentUser.name;
            });
        }

        return {
            pullRequest: state.pullRequest,
            currentUser: state.currentUser,
            currentUserAsReviewer: currentUserAsReviewer()
        }
    }

    return ReactRedux.connect(mapStateToProps)(PullRequestHeaderView);

});
