// # Copyright (C) 2010-2020 RhodeCode GmbH
// #
// # This program is free software: you can redistribute it and/or modify
// # it under the terms of the GNU Affero General Public License, version 3
// # (only), as published by the Free Software Foundation.
// #
// # This program is distributed in the hope that it will be useful,
// # but WITHOUT ANY WARRANTY; without even the implied warranty of
// # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// # GNU General Public License for more details.
// #
// # You should have received a copy of the GNU Affero General Public License
// # along with this program.  If not, see <http://www.gnu.org/licenses/>.
// #
// # This program is dual-licensed. If you wish to learn more about the
// # RhodeCode Enterprise Edition, including its added features, Support services,
// # and proprietary license terms, please see https://rhodecode.com/licenses/


var prButtonLockChecks = {
  'compare': false,
  'reviewers': false
};

/**
 * lock button until all checks and loads are made. E.g reviewer calculation
 * should prevent from submitting a PR
 * @param lockEnabled
 * @param msg
 * @param scope
 */
var prButtonLock = function(lockEnabled, msg, scope) {
      scope = scope || 'all';
      if (scope == 'all'){
          prButtonLockChecks['compare'] = !lockEnabled;
          prButtonLockChecks['reviewers'] = !lockEnabled;
      } else if (scope == 'compare') {
          prButtonLockChecks['compare'] = !lockEnabled;
      } else if (scope == 'reviewers'){
          prButtonLockChecks['reviewers'] = !lockEnabled;
      }
      var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
      if (lockEnabled) {
          $('#pr_submit').attr('disabled', 'disabled');
      }
      else if (checksMeet) {
          $('#pr_submit').prop('disabled', false);
      }

      if (msg) {
          $('#pr_open_message').html(msg);
      }
};


/**
Generate Title and Description for a PullRequest.
In case of 1 commits, the title and description is that one commit
in case of multiple commits, we iterate on them with max N number of commits,
and build description in a form
- commitN
- commitN+1
...

Title is then constructed from branch names, or other references,
replacing '-' and '_' into spaces

* @param sourceRef
* @param elements
* @param limit
* @returns {*[]}
*/
var getTitleAndDescription = function(sourceRefType, sourceRef, elements, limit) {
  var title = '';
  var desc = '';

  $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
      var rawMessage = value['message'];
      desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
  });
  // only 1 commit, use commit message as title
  if (elements.length === 1) {
      var rawMessage = elements[0]['message'];
      title = rawMessage.split('\n')[0];
  }
  else {
      // use reference name
      var normalizedRef = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter()
      var refType = sourceRefType;
      title = 'Changes from {0}: {1}'.format(refType, normalizedRef);
  }

  return [title, desc]
};


window.ReviewersController = function () {
    var self = this;
    this.$loadingIndicator = $('.calculate-reviewers');
    this.$reviewRulesContainer = $('#review_rules');
    this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
    this.$userRule = $('.pr-user-rule-container');
    this.$reviewMembers = $('#review_members');
    this.$observerMembers = $('#observer_members');

    this.currentRequest = null;
    this.diffData = null;
    this.enabledRules = [];
    // sync with db.py entries
    this.ROLE_REVIEWER = 'reviewer';
    this.ROLE_OBSERVER = 'observer'

    //dummy handler, we might register our own later
    this.diffDataHandler = function (data) {};

    this.defaultForbidUsers = function () {
        return [
            {
                'username': 'default',
                'user_id': templateContext.default_user.user_id
            }
        ];
    };

    // init default forbidden users
    this.forbidUsers = this.defaultForbidUsers();

    this.hideReviewRules = function () {
        self.$reviewRulesContainer.hide();
        $(self.$userRule).hide();
    };

    this.showReviewRules = function () {
        self.$reviewRulesContainer.show();
        $(self.$userRule).show();
    };

    this.addRule = function (ruleText) {
        self.showReviewRules();
        self.enabledRules.push(ruleText);
        return '<div>- {0}</div>'.format(ruleText)
    };

    this.increaseCounter = function(role) {
        if (role === self.ROLE_REVIEWER) {
            var $elem = $('#reviewers-cnt')
            var cnt = parseInt($elem.data('count') || 0)
            cnt +=1
            $elem.html(cnt);
            $elem.data('count', cnt);
        }
        else if (role === self.ROLE_OBSERVER) {
            var $elem = $('#observers-cnt');
            var cnt = parseInt($elem.data('count') || 0)
            cnt +=1
            $elem.html(cnt);
            $elem.data('count', cnt);
        }
    }

    this.resetCounter = function () {
        var $elem = $('#reviewers-cnt');

        $elem.data('count', 0);
        $elem.html(0);

        var $elem = $('#observers-cnt');

        $elem.data('count', 0);
        $elem.html(0);
    }

    this.loadReviewRules = function (data) {
        self.diffData = data;

        // reset forbidden Users
        this.forbidUsers = self.defaultForbidUsers();

        // reset state of review rules
        self.$rulesList.html('');

        if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
            // default rule, case for older repo that don't have any rules stored
            self.$rulesList.append(
                self.addRule(_gettext('All reviewers must vote.'))
            );
            return self.forbidUsers
        }

        if (data.rules.forbid_adding_reviewers) {
            $('#add_reviewer_input').remove();
        }

        if (data.rules_data !== undefined && data.rules_data.forbidden_users !== undefined) {
            $.each(data.rules_data.forbidden_users, function(idx, val){
                self.forbidUsers.push(val)
            })
        }

        if (data.rules_humanized !== undefined && data.rules_humanized.length > 0) {
            $.each(data.rules_humanized, function(idx, val) {
                self.$rulesList.append(
                    self.addRule(val)
                )
            })
        } else {
            // we don't have any rules set, so we inform users about it
            self.$rulesList.append(
                self.addRule(_gettext('No additional review rules set.'))
            )
        }

        return self.forbidUsers
    };

    this.emptyTables = function () {
        self.emptyReviewersTable();
        self.emptyObserversTable();

        // Also reset counters.
        self.resetCounter();
    }

    this.emptyReviewersTable = function (withText) {
        self.$reviewMembers.empty();
        if (withText !== undefined) {
            self.$reviewMembers.html(withText)
        }
    };

    this.emptyObserversTable = function (withText) {
        self.$observerMembers.empty();
        if (withText !== undefined) {
            self.$observerMembers.html(withText)
        }
    }

    this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) {

        if (self.currentRequest) {
            // make sure we cleanup old running requests before triggering this again
            self.currentRequest.abort();
        }

        self.$loadingIndicator.show();

        // reset reviewer/observe members
        self.emptyTables();

        prButtonLock(true, null, 'reviewers');
        $('#user').hide(); // hide user autocomplete before load
        $('#observer').hide(); //hide observer autocomplete before load

        // lock PR button, so we cannot send PR before it's calculated
        prButtonLock(true, _gettext('Loading diff ...'), 'compare');

        if (sourceRef.length !== 3 || targetRef.length !== 3) {
            // don't load defaults in case we're missing some refs...
            self.$loadingIndicator.hide();
            return
        }

        var url = pyroutes.url('repo_default_reviewers_data',
            {
                'repo_name': templateContext.repo_name,
                'source_repo': sourceRepo,
                'source_ref_type': sourceRef[0],
                'source_ref_name': sourceRef[1],
                'source_ref': sourceRef[2],
                'target_repo': targetRepo,
                'target_ref': targetRef[2],
                'target_ref_type': targetRef[0],
                'target_ref_name': targetRef[1]
            });

        self.currentRequest = $.ajax({
            url: url,
            headers: {'X-PARTIAL-XHR': true},
            type: 'GET',
            success: function (data) {

                self.currentRequest = null;

                // review rules
                self.loadReviewRules(data);
                var diffHandled = self.handleDiffData(data["diff_info"]);
                if (diffHandled === false) {
                    return
                }

                for (var i = 0; i < data.reviewers.length; i++) {
                    var reviewer = data.reviewers[i];
                    // load reviewer rules from the repo data
                    self.addMember(reviewer, reviewer.reasons, reviewer.mandatory, reviewer.role);
                }


                self.$loadingIndicator.hide();
                prButtonLock(false, null, 'reviewers');

                $('#user').show(); // show user autocomplete before load
                $('#observer').show(); // show observer autocomplete before load

                var commitElements = data["diff_info"]['commits'];

                if (commitElements.length === 0) {
                    var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
                        _gettext('There are no commits to merge.'));
                    prButtonLock(true, noCommitsMsg, 'all');

                } else {
                    // un-lock PR button, so we cannot send PR before it's calculated
                    prButtonLock(false, null, 'compare');
                }

            },
            error: function (jqXHR, textStatus, errorThrown) {
                var prefix = "Loading diff and reviewers/observers failed\n"
                var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
                ajaxErrorSwal(message);
            }
        });

    };

    // check those, refactor
    this.removeMember = function (reviewer_id, mark_delete) {
        var reviewer = $('#reviewer_{0}'.format(reviewer_id));

        if (typeof (mark_delete) === undefined) {
            mark_delete = false;
        }

        if (mark_delete === true) {
            if (reviewer) {
                // now delete the input
                $('#reviewer_{0} input'.format(reviewer_id)).remove();
                $('#reviewer_{0}_rules input'.format(reviewer_id)).remove();
                // mark as to-delete
                var obj = $('#reviewer_{0}_name'.format(reviewer_id));
                obj.addClass('to-delete');
                obj.css({"text-decoration": "line-through", "opacity": 0.5});
            }
        } else {
            $('#reviewer_{0}'.format(reviewer_id)).remove();
        }
    };

    this.addMember = function (reviewer_obj, reasons, mandatory, role) {

        var id = reviewer_obj.user_id;
        var username = reviewer_obj.username;

        reasons = reasons || [];
        mandatory = mandatory || false;
        role = role || self.ROLE_REVIEWER

        // register current set IDS to check if we don't have this ID already in
        // and prevent duplicates
        var currentIds = [];

        $.each($('.reviewer_entry'), function (index, value) {
            currentIds.push($(value).data('reviewerUserId'))
        })

        var userAllowedReview = function (userId) {
            var allowed = true;
            $.each(self.forbidUsers, function (index, member_data) {
                if (parseInt(userId) === member_data['user_id']) {
                    allowed = false;
                    return false // breaks the loop
                }
            });
            return allowed
        };

        var userAllowed = userAllowedReview(id);

        if (!userAllowed) {
            alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
        } else {
            // only add if it's not there
            var alreadyReviewer = currentIds.indexOf(id) != -1;

            if (alreadyReviewer) {
                alert(_gettext('User `{0}` already in reviewers/observers').format(username));
            } else {

                var reviewerEntry = renderTemplate('reviewMemberEntry', {
                    'member': reviewer_obj,
                    'mandatory': mandatory,
                    'role': role,
                    'reasons': reasons,
                    'allowed_to_update': true,
                    'review_status': 'not_reviewed',
                    'review_status_label': _gettext('Not Reviewed'),
                    'user_group': reviewer_obj.user_group,
                    'create': true,
                    'rule_show': true,
                    'rhodecode_user': templateContext.rhodecode_user
                })

                if (role === self.ROLE_REVIEWER) {
                    $(self.$reviewMembers).append(reviewerEntry);
                    self.increaseCounter(self.ROLE_REVIEWER);
                    $('#reviewer-empty-msg').remove()
                }
                else if (role === self.ROLE_OBSERVER) {
                    $(self.$observerMembers).append(reviewerEntry);
                    self.increaseCounter(self.ROLE_OBSERVER);
                    $('#observer-empty-msg').remove();
                }

                tooltipActivate();
            }
        }

    };

    this.startAiCodeReview = function (repo_name, pull_request_id) {
        const url = `/${repo_name}/pull-request/${pull_request_id}/ai/review`
        const postData = {};
        const success = function (o) {
            window.location.reload();
        };
        ajaxPOST(url, postData, success);
    };

    this.updateReviewers = function (repo_name, pull_request_id, role) {
        if (role === 'reviewer') {
            var postData = $('#reviewers input').serialize();
            _updatePullRequest(repo_name, pull_request_id, postData);
        } else if (role === 'observer') {
            var postData = $('#observers input').serialize();
            _updatePullRequest(repo_name, pull_request_id, postData);
        }
    };

    this.handleDiffData = function (data) {
        return self.diffDataHandler(data)
    }
};


var _updatePullRequest = function(repo_name, pull_request_id, postData) {
    var url = pyroutes.url(
        'pullrequest_update',
        {"repo_name": repo_name, "pull_request_id": pull_request_id});
    if (typeof postData === 'string' ) {
        postData += '&csrf_token=' + CSRF_TOKEN;
    } else {
        postData.csrf_token = CSRF_TOKEN;
    }

    var success = function(o) {
        var redirectUrl = o['redirect_url'];
        if (redirectUrl !== undefined && redirectUrl !== null && redirectUrl !== '') {
            window.location = redirectUrl;
        } else {
            window.location.reload();
        }
    };

    ajaxPOST(url, postData, success);
};

/**
 * PULL REQUEST update commits
 */
var updateCommits = function(repo_name, pull_request_id, force) {
    var postData = {
        'update_commits': true
    };
    if (force !== undefined && force === true) {
        postData['force_refresh'] = true
    }
    _updatePullRequest(repo_name, pull_request_id, postData);
};


/**
 * PULL REQUEST edit info
 */
var editPullRequest = function(repo_name, pull_request_id, title, description, renderer) {
    var url = pyroutes.url(
        'pullrequest_update',
        {"repo_name": repo_name, "pull_request_id": pull_request_id});

    var postData = {
        'title': title,
        'description': description,
        'description_renderer': renderer,
        'edit_pull_request': true,
        'csrf_token': CSRF_TOKEN
    };
    var success = function(o) {
        window.location.reload();
    };
    ajaxPOST(url, postData, success);
};


/**
 * autocomplete handler for reviewers/observers
 */
var autoCompleteHandler = function (inputId, controller, role) {

    return function (element, data) {
        var mandatory = false;
        var reasons = [_gettext('added manually by "{0}"').format(
            templateContext.rhodecode_user.username)];

        // add whole user groups
        if (data.value_type == 'user_group') {
            reasons.push(_gettext('member of "{0}"').format(data.value_display));

            $.each(data.members, function (index, member_data) {
                var reviewer = member_data;
                reviewer['user_id'] = member_data['id'];
                reviewer['gravatar_link'] = member_data['icon_link'];
                reviewer['user_link'] = member_data['profile_link'];
                reviewer['rules'] = [];
                controller.addMember(reviewer, reasons, mandatory, role);
            })
        }
        // add single user
        else {
            var reviewer = data;
            reviewer['user_id'] = data['id'];
            reviewer['gravatar_link'] = data['icon_link'];
            reviewer['user_link'] = data['profile_link'];
            reviewer['rules'] = [];
            controller.addMember(reviewer, reasons, mandatory, role);
        }

        $(inputId).val('');
    }
}

let updateCloseBranchSetting = function(repo_name, pull_request_id, close_branch_before_merging) {
    const postData = {
        'close_branch_before_merging': close_branch_before_merging,
    };
    _updatePullRequest(repo_name, pull_request_id, postData);
}

/**
 * Reviewer autocomplete
 */
var ReviewerAutoComplete = function (inputId, controller) {
    var self = this;
    self.controller = controller;
    self.inputId = inputId;
    var handler = autoCompleteHandler(inputId, controller, controller.ROLE_REVIEWER);

    $(inputId).autocomplete({
        serviceUrl: pyroutes.url('user_autocomplete_data'),
        minChars: 2,
        maxHeight: 400,
        deferRequestBy: 300, //miliseconds
        showNoSuggestionNotice: true,
        tabDisabled: true,
        autoSelectFirst: true,
        params: {
            user_id: templateContext.rhodecode_user.user_id,
            user_groups: true,
            user_groups_expand: true,
            skip_default_user: true
        },
        formatResult: autocompleteFormatResult,
        lookupFilter: autocompleteFilterResult,
        onSelect: handler
    });
};

/**
 * Observers autocomplete
 */
var ObserverAutoComplete = function(inputId, controller) {
    var self = this;
    self.controller = controller;
    self.inputId = inputId;
    var handler = autoCompleteHandler(inputId, controller, controller.ROLE_OBSERVER);

    $(inputId).autocomplete({
        serviceUrl: pyroutes.url('user_autocomplete_data'),
        minChars: 2,
        maxHeight: 400,
        deferRequestBy: 300, //miliseconds
        showNoSuggestionNotice: true,
        tabDisabled: true,
        autoSelectFirst: true,
        params: {
            user_id: templateContext.rhodecode_user.user_id,
            user_groups: true,
            user_groups_expand: true,
            skip_default_user: true
        },
        formatResult: autocompleteFormatResult,
        lookupFilter: autocompleteFilterResult,
        onSelect: handler
    });
}


window.VersionController = function () {
    var self = this;
    this.$verSource = $('input[name=ver_source]');
    this.$verTarget = $('input[name=ver_target]');
    this.$showVersionDiff = $('#show-version-diff');

    this.adjustRadioSelectors = function (curNode) {
        var getVal = function (item) {
            if (item === 'latest') {
                return Number.MAX_SAFE_INTEGER
            }
            else {
                return parseInt(item)
            }
        };

        var curVal = getVal($(curNode).val());
        var cleared = false;

        $.each(self.$verSource, function (index, value) {
            var elVal = getVal($(value).val());

            if (elVal > curVal) {
                if ($(value).is(':checked')) {
                    cleared = true;
                }
                $(value).attr('disabled', 'disabled');
                $(value).removeAttr('checked');
                $(value).css({'opacity': 0.1});
            }
            else {
                $(value).css({'opacity': 1});
                $(value).removeAttr('disabled');
            }
        });

        if (cleared) {
            // if we unchecked an active, set the next one to same loc.
            $(this.$verSource).filter('[value={0}]'.format(
                curVal)).attr('checked', 'checked');
        }

        self.setLockAction(false,
            $(curNode).data('verPos'),
            $(this.$verSource).filter(':checked').data('verPos')
        );
    };


    this.attachVersionListener = function () {
        self.$verTarget.on('change', function (e) {
            self.adjustRadioSelectors(this)
        });
        self.$verSource.on('change', function (e) {
            self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
        });
    };

    this.init = function () {

        var curNode = self.$verTarget.filter(':checked');
        self.adjustRadioSelectors(curNode);
        self.setLockAction(true);
        self.attachVersionListener();

    };

    this.setLockAction = function (state, selectedVersion, otherVersion) {
        var $showVersionDiff = this.$showVersionDiff;

        if (state) {
            $showVersionDiff.attr('disabled', 'disabled');
            $showVersionDiff.addClass('disabled');
            $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
        }
        else {
            $showVersionDiff.prop('disabled', false);
            $showVersionDiff.removeClass('disabled');

            if (selectedVersion == otherVersion) {
                $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
            } else {
                $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
            }
        }

    };

    this.showVersionDiff = function () {
        var target = self.$verTarget.filter(':checked');
        var source = self.$verSource.filter(':checked');

        if (target.val() && source.val()) {
            var params = {
                'pull_request_id': templateContext.pull_request_data.pull_request_id,
                'repo_name': templateContext.repo_name,
                'version': target.val(),
                'from_version': source.val()
            };
            window.location = pyroutes.url('pullrequest_show', params)
        }

        return false;
    };

    this.toggleVersionView = function (elem) {

        if (this.$showVersionDiff.is(':visible')) {
            $('.version-pr').hide();
            this.$showVersionDiff.hide();
            $(elem).html($(elem).data('toggleOn'))
        } else {
            $('.version-pr').show();
            this.$showVersionDiff.show();
            $(elem).html($(elem).data('toggleOff'))
        }

        return false
    };

};


window.UpdatePrController = function () {
    var self = this;
    this.$updateCommits = $('#update_commits');
    this.$updateCommitsSwitcher = $('#update_commits_switcher');

    this.lockUpdateButton = function (label) {
        self.$updateCommits.attr('disabled', 'disabled');
        self.$updateCommitsSwitcher.attr('disabled', 'disabled');

        self.$updateCommits.addClass('disabled');
        self.$updateCommitsSwitcher.addClass('disabled');

        self.$updateCommits.removeClass('btn-primary');
        self.$updateCommitsSwitcher.removeClass('btn-primary');

        self.$updateCommits.text(_gettext(label));
    };

    this.isUpdateLocked = function () {
        return self.$updateCommits.attr('disabled') !== undefined;
    };

    this.updateCommits = function (curNode) {
        if (self.isUpdateLocked()) {
            return
        }
        self.lockUpdateButton(_gettext('Updating...'));
        updateCommits(
            templateContext.repo_name,
            templateContext.pull_request_data.pull_request_id);
    };

    this.forceUpdateCommits = function () {
        if (self.isUpdateLocked()) {
            return
        }
        self.lockUpdateButton(_gettext('Force updating...'));
        var force = true;
        updateCommits(
            templateContext.repo_name,
            templateContext.pull_request_data.pull_request_id, force);
    };
};


/**
 * Reviewer display panel
 */
window.ReviewersPanel = {
    editButton: null,
    closeButton: null,
    addButton: null,
    removeButtons: null,
    reviewRules: null,
    setReviewers: null,
    controller: null,

    setSelectors: function () {
        var self = this;
        self.editButton = $('#open_edit_reviewers');
        self.closeButton =$('#close_edit_reviewers');
        self.addButton = $('#add_reviewer');
        self.removeButtonsSelector = '.reviewer_member_remove,.reviewer_member_mandatory_remove';
    },

    init: function (controller, reviewRules, setReviewers) {
        var self = this;
        self.setSelectors();

        self.controller = controller;
        self.reviewRules = reviewRules;
        self.setReviewers = setReviewers;

        self.editButton.on('click', function (e) {
            self.edit();
        });
        self.closeButton.on('click', function (e) {
            self.close();
            self.renderReviewers();
        });

        self.renderReviewers();

    },

    renderReviewers: function () {
        var self = this;

        if (self.setReviewers.reviewers === undefined) {
            return
        }
        if (self.setReviewers.reviewers.length === 0) {
            self.controller.emptyReviewersTable('<tr id="reviewer-empty-msg"><td colspan="6">No reviewers</td></tr>');
            return
        }

        self.controller.emptyReviewersTable();

        $.each(self.setReviewers.reviewers, function (key, val) {

            var member = val;
            if (member.role === self.controller.ROLE_REVIEWER) {
                var entry = renderTemplate('reviewMemberEntry', {
                    'member': member,
                    'mandatory': member.mandatory,
                    'role': member.role,
                    'reasons': member.reasons,
                    'allowed_to_update': member.allowed_to_update,
                    'review_status': member.review_status,
                    'review_status_label': member.review_status_label,
                    'user_group': member.user_group,
                    'create': false,
                    'rhodecode_user': templateContext.rhodecode_user
                });

                $(self.controller.$reviewMembers).append(entry)
            }
        });

        tooltipActivate();
    },

    edit: function (event) {
        var self = this;
        self.editButton.hide();
        self.closeButton.show();
        self.addButton.show();
        $(self.removeButtonsSelector).css('visibility', 'visible');
        // review rules
        self.controller.loadReviewRules(this.reviewRules);
    },

    close: function (event) {
        var self = this;
        this.editButton.show();
        this.closeButton.hide();
        this.addButton.hide();
        $(this.removeButtonsSelector).css('visibility', 'hidden');
        // hide review rules
        self.controller.hideReviewRules();
    }
};

/**
 * Reviewer display panel
 */
window.ObserversPanel = {
    editButton: null,
    closeButton: null,
    addButton: null,
    removeButtons: null,
    reviewRules: null,
    setReviewers: null,
    controller: null,

    setSelectors: function () {
        var self = this;
        self.editButton = $('#open_edit_observers');
        self.closeButton =$('#close_edit_observers');
        self.addButton = $('#add_observer');
        self.removeButtonsSelector = '.observer_member_remove,.observer_member_mandatory_remove';
    },

    init: function (controller, reviewRules, setReviewers) {
        var self = this;
        self.setSelectors();

        self.controller = controller;
        self.reviewRules = reviewRules;
        self.setReviewers = setReviewers;

        self.editButton.on('click', function (e) {
            self.edit();
        });
        self.closeButton.on('click', function (e) {
            self.close();
            self.renderObservers();
        });

        self.renderObservers();

    },

    renderObservers: function () {
        var self = this;
        if (self.setReviewers.observers === undefined) {
            return
        }
        if (self.setReviewers.observers.length === 0) {
            self.controller.emptyObserversTable('<tr id="observer-empty-msg"><td colspan="6">No observers</td></tr>');
            return
        }

        self.controller.emptyObserversTable();

        $.each(self.setReviewers.observers, function (key, val) {
            var member = val;
            if (member.role === self.controller.ROLE_OBSERVER) {
                var entry = renderTemplate('reviewMemberEntry', {
                    'member': member,
                    'mandatory': member.mandatory,
                    'role': member.role,
                    'reasons': member.reasons,
                    'allowed_to_update': member.allowed_to_update,
                    'review_status': member.review_status,
                    'review_status_label': member.review_status_label,
                    'user_group': member.user_group,
                    'create': false,
                    'rhodecode_user': templateContext.rhodecode_user
                });

                $(self.controller.$observerMembers).append(entry)
            }
        });

        tooltipActivate();
    },

    edit: function (event) {
        this.editButton.hide();
        this.closeButton.show();
        this.addButton.show();
        $(this.removeButtonsSelector).css('visibility', 'visible');
    },

    close: function (event) {
        this.editButton.show();
        this.closeButton.hide();
        this.addButton.hide();
        $(this.removeButtonsSelector).css('visibility', 'hidden');
    }

};

window.PRDetails = {
    editButton: null,
    closeButton: null,
    deleteButton: null,
    viewFields: null,
    editFields: null,

    setSelectors: function () {
        var self = this;
        self.editButton = $('#open_edit_pullrequest')
        self.closeButton = $('#close_edit_pullrequest')
        self.deleteButton = $('#delete_pullrequest')
        self.viewFields = $('#pr-desc, #pr-title')
        self.editFields = $('#pr-desc-edit, #pr-title-edit, .pr-save')
    },

    init: function () {
        var self = this;
        self.setSelectors();
        self.editButton.on('click', function (e) {
            self.edit();
        });
        self.closeButton.on('click', function (e) {
            self.view();
        });
    },

    edit: function (event) {
        var cmInstance = $('#pr-description-input').get(0).MarkupForm.cm;
        this.viewFields.hide();
        this.editButton.hide();
        this.deleteButton.hide();
        this.closeButton.show();
        this.editFields.show();
        cmInstance.refresh();
    },

    view: function (event) {
        this.editButton.show();
        this.deleteButton.show();
        this.editFields.hide();
        this.closeButton.hide();
        this.viewFields.show();
    }
};

/**
 * OnLine presence using channelstream
 */
window.ReviewerPresenceController = function (channel) {
    var self = this;
    this.channel = channel;
    this.users = {};

    this.storeUsers = function (users) {
        self.users = {}
        $.each(users, function (index, value) {
            var userId = value.state.id;
            self.users[userId] = value.state;
        })
    }

    this.render = function () {
        $.each($('.reviewer_entry'), function (index, value) {
            var userData = $(value).data();
            if (self.users[userData.reviewerUserId] !== undefined) {
                $(value).find('.presence-state').show();
            } else {
                $(value).find('.presence-state').hide();
            }
        })
    };

    this.handlePresence = function (data) {
        if (data.type == 'presence' && data.channel === self.channel) {
            this.storeUsers(data.users);
            this.render();
        }
    };

    this.handleChannelUpdate = function (data) {
        if (data.channel === this.channel) {
            this.storeUsers(data.state.users);
            this.render();
        }

    };

    /* subscribe to the current presence */
    $.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this));
    /* subscribe to updates e.g connect/disconnect */
    $.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this));

};

window.refreshCommentsSuccess = function(targetNode, counterNode, extraCallback) {
    var $targetElem = targetNode;
    var $counterElem = counterNode;

    return function (data) {
        var newCount = $(data).data('counter');
        if (newCount !== undefined) {
            var callback = function () {
                $counterElem.animate({'opacity': 1.00}, 200)
                $counterElem.html(newCount);
            };
            $counterElem.animate({'opacity': 0.15}, 200, callback);
        }

        $targetElem.css('opacity', 1);
        $targetElem.html(data);
        tooltipActivate();

        if (extraCallback !== undefined) {
            extraCallback(data)
        }
    }
}

window.refreshComments = function (version) {
    version = version || templateContext.pull_request_data.pull_request_version || '';

    // Pull request case
    if (templateContext.pull_request_data.pull_request_id !== null) {
        var params = {
            'pull_request_id': templateContext.pull_request_data.pull_request_id,
            'repo_name': templateContext.repo_name,
            'version': version,
        };
        var loadUrl = pyroutes.url('pullrequest_comments', params);
    } // commit case
    else {
        return
    }

    var currentIDs = []
    $.each($('.comment'), function (idx, element) {
        currentIDs.push($(element).data('commentId'));
    });
    var data = {"comments": currentIDs};

    var $targetElem = $('.comments-content-table');
    $targetElem.css('opacity', 0.3);
    var $counterElem = $('#comments-count');
    var success = refreshCommentsSuccess($targetElem, $counterElem);
    ajaxPOST(loadUrl, data, success, null, {})

}

window.refreshTODOs = function (version) {
    version = version || templateContext.pull_request_data.pull_request_version || '';
    // Pull request case
    if (templateContext.pull_request_data.pull_request_id !== null) {
        var params = {
            'pull_request_id': templateContext.pull_request_data.pull_request_id,
            'repo_name': templateContext.repo_name,
            'version': version,
        };
        var loadUrl = pyroutes.url('pullrequest_todos', params);
    } // commit case
    else {
        return
    }

    var currentIDs = []
    $.each($('.comment'), function (idx, element) {
        currentIDs.push($(element).data('commentId'));
    });

    var data = {"comments": currentIDs};
    var $targetElem = $('.todos-content-table');
    $targetElem.css('opacity', 0.3);
    var $counterElem = $('#todos-count');
    var success = refreshCommentsSuccess($targetElem, $counterElem);

    ajaxPOST(loadUrl, data, success, null, {})

}

window.refreshDraftComments = function () {

    // Pull request case
    if (templateContext.pull_request_data.pull_request_id !== null) {
        var params = {
            'pull_request_id': templateContext.pull_request_data.pull_request_id,
            'repo_name': templateContext.repo_name,
        };
        var loadUrl = pyroutes.url('pullrequest_drafts', params);
    } // commit case
    else {
        return
    }

    var data = {};

    var $targetElem = $('.drafts-content-table');
    $targetElem.css('opacity', 0.3);
    var $counterElem = $('#drafts-count');
    var extraCallback = function(data) {
        if ($(data).data('counter') == 0){
            $('#draftsTable').hide();
        } else {
            $('#draftsTable').show();
        }
        // uncheck on load the select all checkbox
        $('[name=select_all_drafts]').prop('checked', 0);
    }
    var success = refreshCommentsSuccess($targetElem, $counterElem, extraCallback);

    ajaxPOST(loadUrl, data, success, null, {})
};

window.refreshAllComments = function (version) {
    version = version || templateContext.pull_request_data.pull_request_version || '';

    refreshComments(version);
    refreshTODOs(version);
};

window.sidebarComment = function (commentId) {
    var jsonData = $('#commentHovercard{0}'.format(commentId)).data('commentJsonB64');
    if (!jsonData) {
        return 'Failed to load comment {0}'.format(commentId)
    }
    var funcData = JSON.parse(atob(jsonData));
    return renderTemplate('sideBarCommentHovercard', funcData)
};
