/* global describe, it, before, after */
"use strict";
const InstanceManager = require("../InstanceManager.js");
const expect = require("chai").expect;
const arangojs = require("arangojs");
const rp = require("request-promise");

describe("Move shards", function() {
  let instanceManager = new InstanceManager("shard-move");
  let db;
  let servers = [];
  before(function() {
    return instanceManager
      .startCluster(1, 2, 3)
      .then(() => {
        db = arangojs({
          url: instanceManager.getEndpointUrl(),
          databaseName: "_system"
        });
        return db
          .collection("testcollection")
          .create({ shards: 1, replicationFactor: 2 });
      })
      .then(() => {
        return rp({
          url: instanceManager.getEndpointUrl() +
            "/_db/_system/_admin/aardvark/cluster/DBServers",
          json: true
        });
      })
      .then(_servers => {
        servers = _servers;
      });
  });
  after(function() {
    return instanceManager.cleanup();
  });

  it("should allow moving shards while writing", function() {
    let stopMoving = false;

    let moveShards = function() {
      if (moveShards.stop) {
        return Promise.resolve();
      }

      let waitShardMoved = function(newLeader, numServers, num) {
        if (num > 500) {
          return Promise.reject(new Error(
            "Shard did not come into sync after " +
              num +
              " tries. " + newLeader
          ));
        }
        return rp({
          url: instanceManager.getEndpointUrl() +
            "/_db/_system/_admin/cluster/shardDistribution",
          json: true
        })
        .then(shardDistribution => {
          let shards = shardDistribution.results.testcollection.Current;
          let shardKey = Object.keys(shards)[0];
          // upon leader resign this might be undefined
          let currentServers = [];
          if (shards[shardKey].leader) {
            currentServers.push(shards[shardKey].leader);
          }
          return currentServers.concat(shards[shardKey].followers);
        })
        .then(is => {
          return (
            is.length == numServers &&
            is[0] === newLeader
          );
        })
        .then(equal => {
          if (!equal) {
            return new Promise((resolve, reject) => {
              setTimeout(resolve, 100);
            }).then(() => {
              return waitShardMoved(newLeader, numServers, num + 1);
            });
          }
        });
      };

      return rp({
        url: instanceManager.getEndpointUrl() +
          "/_db/_system/_admin/cluster/shardDistribution",
        json: true
      })
      .then(shardDistribution => {
        let shards = shardDistribution.results.testcollection.Plan;
        let shardKey = Object.keys(shards)[0];
        let is = [shards[shardKey].leader].concat(shards[shardKey].followers);
        let freeServer = servers.filter(
          server => is.indexOf(server.name) == -1
        )[0];

        if (freeServer === undefined) {
          throw new Error('Don\'t have a free server! Servers: ' + JSON.stringify(servers) + ', current: ' + JSON.stringify(is));
        }
        let should = is.slice();
        should[0] = freeServer.name;
        let move = {
          collection: "testcollection",
          database: "_system",
          shard: shardKey,
          fromServer: servers.filter(
            server => server.name == shards[shardKey].leader
          )[0].id,
          toServer: freeServer.id
        };
        return rp({
          url: instanceManager.getEndpointUrl() +
            "/_db/_system/_admin/cluster/moveShard",
          json: true,
          body: move,
          method: "POST"
        }).then(() => {
          return should;
        });
      })
      .then(should => {
        return waitShardMoved(should[0], should.length, 1);
      })
      .then(() => {
        return moveShards();
      });
    };

    let insertDocuments = function() {
      return [...Array(10000).keys()].reduce((promise, key) => {
        return promise.then(() => {
          return db.collection("testcollection").save({ hallooo: key });
        });
      }, Promise.resolve());
    };

    let movePromise = moveShards();

    return insertDocuments()
      .then(() => {
        moveShards.stop = true;
        return movePromise;
      })
      .then(() => {
        return db.collection("testcollection").count();
      })
      .then(count => {
        expect(count.count).to.equal(10000);
        return db.collection("testcollection").all();
      })
      .then(cursor => {
        return cursor.all();
      })
      .then(all => {
        all = all.map(doc => doc.hallooo);
        all.sort((a, b) => (a < b ? -1 : 1));
        expect(all).to.deep.equal([...Array(10000).keys()]);
      });
  });
});

@@ -1689,33 +1689,40 @@
 %7B%0A          let 
-s
+currentS
 hards = shardDis
@@ -1792,33 +1792,40 @@
 y = Object.keys(
-s
+currentS
 hards)%5B0%5D;%0A     
@@ -1924,17 +1924,24 @@
     if (
-s
+currentS
 hards%5Bsh
@@ -1994,62 +1994,391 @@
 ush(
-shards%5BshardKey%5D.leader);%0A          %7D%0A          return
+currentShards%5BshardKey%5D.leader);%0A          %7D%0A%0A          let plannedShards = shardDistribution.results.testcollection.Plan;%0A          let plannedServers = %5B%5D;%0A          if (plannedShards%5BshardKey%5D.leader) %7B%0A            plannedServers.push(plannedShards%5BshardKey%5D.leader);%0A          %7D%0A          return %5B%0A            plannedServers.concat(plannedShards%5BshardKey%5D.followers),%0A           
  cur
@@ -2388,33 +2388,40 @@
 tServers.concat(
-s
+currentS
 hards%5BshardKey%5D.
@@ -2422,32 +2422,45 @@
 dKey%5D.followers)
+,%0A          %5D
 ;%0A        %7D)%0A   
@@ -2474,16 +2474,91 @@
 hen(
-is
+result
  =%3E %7B%0A
+          let planned = result%5B0%5D;%0A          let current = result%5B1%5D;%0A%0A
     
@@ -2584,18 +2584,23 @@
         
-is
+current
 .length 
@@ -2632,27 +2632,154 @@
     
-is%5B0%5D === newLeader
+current.length == planned.length &&%0A            current%5B0%5D === newLeader &&%0A            current.every((server, index) =%3E planned%5Bindex%5D == server)
 %0A   
@@ -2813,21 +2813,24 @@
   .then(
-equal
+finished
  =%3E %7B%0A  
@@ -2842,21 +2842,24 @@
    if (!
-equal
+finished
 ) %7B%0A    

Make it (hopefully) a bit more resilient? :S