// come up with random priorities and make sure that the right member gets
// elected. then kill that member and make sure the next one gets elected.

(function() {

    "use strict";

    load("jstests/replsets/rslib.js");

    var rs = new ReplSetTest({name: 'testSet', nodes: 3, nodeOptions: {verbose: 2}});
    var nodes = rs.startSet();
    rs.initiate();

    var master = rs.getPrimary();

    var everyoneOkSoon = function() {
        var status;
        assert.soon(function() {
            var ok = true;
            status = master.adminCommand({replSetGetStatus: 1});

            if (!status.members) {
                return false;
            }

            for (var i in status.members) {
                if (status.members[i].health == 0) {
                    continue;
                }
                ok &= status.members[i].state == 1 || status.members[i].state == 2;
            }
            return ok;
        }, tojson(status));
    };

    var checkPrimaryIs = function(node) {

        print("nreplsets_priority1.js checkPrimaryIs(" + node.host + ")");

        var status;

        assert.soon(function() {
            var ok = true;

            try {
                status = master.adminCommand({replSetGetStatus: 1});
            } catch (e) {
                print(e);
                print("nreplsets_priority1.js checkPrimaryIs reconnecting");
                reconnect(master);
                status = master.adminCommand({replSetGetStatus: 1});
            }

            var str = "goal: " + node.host + "==1 states: ";
            if (!status || !status.members) {
                return false;
            }
            status.members.forEach(function(m) {
                str += m.name + ": " + m.state + " ";

                if (m.name == node.host) {
                    ok &= m.state == 1;
                } else {
                    ok &= m.state != 1 || (m.state == 1 && m.health == 0);
                }
            });
            print();
            print(str);
            print();

            occasionally(function() {
                print("\nstatus:");
                printjson(status);
                print();
            }, 15);

            return ok;
        }, node.host + '==1', 240000, 1000);

        everyoneOkSoon();
    };

    everyoneOkSoon();

    print("\n\nreplsets_priority1.js initial sync");

    // intial sync
    master.getDB("foo").bar.insert({x: 1});
    rs.awaitReplication();

    print("\n\nreplsets_priority1.js starting loop");

    var n = 5;
    for (var i = 0; i < n; i++) {
        print("Round " + i + ": FIGHT!");

        var max = null;
        var second = null;
        reconnect(master);
        var config = master.getDB("local").system.replset.findOne();

        var version = config.version;
        config.version++;

        for (var j = 0; j < config.members.length; j++) {
            var priority = Math.random() * 100;
            print("random priority : " + priority);
            config.members[j].priority = priority;

            if (!max || priority > max.priority) {
                max = config.members[j];
            }
        }

        for (var j = 0; j < config.members.length; j++) {
            if (config.members[j] == max) {
                continue;
            }
            if (!second || config.members[j].priority > second.priority) {
                second = config.members[j];
            }
        }

        print("\n\nreplsets_priority1.js max is " + max.host + " with priority " + max.priority +
              ", reconfiguring...");

        var count = 0;
        while (config.version != version && count < 100) {
            reconnect(master);

            occasionally(function() {
                print("version is " + version + ", trying to update to " + config.version);
            });

            try {
                master.adminCommand({replSetReconfig: config});
                master = rs.getPrimary();
                reconnect(master);

                version = master.getDB("local").system.replset.findOne().version;
            } catch (e) {
                print("nreplsets_priority1.js Caught exception: " + e);
            }

            count++;
        }

        print("\nreplsets_priority1.js wait for 2 slaves");

        assert.soon(function() {
            rs.getPrimary();
            return rs.liveNodes.slaves.length == 2;
        }, "2 slaves");

        print("\nreplsets_priority1.js wait for new config version " + config.version);

        assert.soon(function() {
            var versions = [0, 0];
            rs.liveNodes.slaves[0].setSlaveOk();
            versions[0] = rs.liveNodes.slaves[0].getDB("local").system.replset.findOne().version;
            rs.liveNodes.slaves[1].setSlaveOk();
            versions[1] = rs.liveNodes.slaves[1].getDB("local").system.replset.findOne().version;
            return versions[0] == config.version && versions[1] == config.version;
        });

        print("replsets_priority1.js awaitReplication");

        // the reconfiguration needs to be replicated! the hb sends it out
        // separately from the repl
        rs.awaitReplication();

        print("reconfigured.  Checking statuses.");

        checkPrimaryIs(max);

        // Wait for election oplog entry to be replicated, to avoid rollbacks later on.
        rs.awaitReplication();

        print("rs.stop");

        rs.stop(max._id);

        master = rs.getPrimary();

        print("\nkilled max primary.  Checking statuses.");

        print("second is " + second.host + " with priority " + second.priority);
        checkPrimaryIs(second);

        // Wait for election oplog entry to be replicated, to avoid rollbacks later on.
        rs.awaitReplication();

        print("restart max " + max._id);

        rs.restart(max._id);
        master = rs.getPrimary();

        print("max restarted.  Checking statuses.");
        checkPrimaryIs(max);

        // Wait for election oplog entry to be replicated, to avoid rollbacks later on.
        rs.awaitReplication();
    }

})();
