'use strict';

const expect = require('chai').expect,
  f = require('util').format,
  p = require('path'),
  fs = require('fs'),
  ObjectId = require('bson').ObjectId,
  ReplSetState = require('../../../lib/core/topologies/replset_state');

describe('ReplicaSet state', function() {
  const path = p.resolve(__dirname, '../../spec/server-discovery-and-monitoring/rs');

  fs.readdirSync(path)
    .filter(x => x.indexOf('.json') !== -1)
    .forEach(x => {
      const testData = require(f('%s/%s', path, x));
      const description = testData.description;
      it(description, function(done) {
        if (
          description.match(/Repeated ismaster response must be processed/) ||
          description.match(/Primary mismatched me is not removed/) ||
          description.match(/replicaSet URI option causes starting topology to be RSNP/) ||
          description.match(/Discover secondary with directConnection URI option/) ||
          description.match(/Discover ghost with directConnection URI option/) ||
          description.match(/topologyVersion/)
        ) {
          this.skip();
          return;
        }

        executeEntry(testData, done);
      });
    });
});

function executeEntry(testData, callback) {
  var uri = testData.uri;
  var phases = testData.phases;

  // Get replicaset name if any
  var match = uri.match(/replicaSet=[a-z|A-Z|0-9]*/);
  var replicaSet = match ? match.toString().split(/=/)[1] : null;

  // Replicaset
  // Create a replset state
  var state = new ReplSetState({ setName: replicaSet });

  // Get all the server instances
  var parts = uri
    .split('mongodb://')[1]
    .split('/')[0]
    .split(',');

  // For each of the servers
  parts.forEach(function(x) {
    var params = x.split(':');
    state.update({
      name: f('%s:%s', params[0], params[1] ? parseInt(params[1], 10) : 27017),
      lastIsMaster: function() {
        return null;
      },
      equals: function(s) {
        if (typeof s === 'string') return s === this.name;
        return s.name === this.name;
      },
      destroy: function() {}
    });
  });

  // Run each phase
  executePhases(phases, state, callback);
}

function executePhases(phases, state, callback) {
  if (phases.length === 0) {
    return callback(null, null);
  }

  executePhase(phases.shift(), state, err => {
    if (err) return callback(err, null);
    return executePhases(phases, state, callback);
  });
}

function executePhase(phase, state, callback) {
  var responses = phase.responses;
  var outcome = phase.outcome;

  // Apply all the responses
  responses.forEach(function(x) {
    if (Object.keys(x[1]).length === 0) {
      state.remove({
        name: x[0],
        lastIsMaster: function() {
          return null;
        },
        equals: function(s) {
          if (typeof s === 'string') return s === this.name;
          return s.name === this.name;
        },
        destroy: function() {}
      });
    } else {
      var ismaster = x[1];
      if (ismaster.electionId) ismaster.electionId = new ObjectId(ismaster.electionId.$oid);

      state.update({
        name: x[0],
        lastIsMaster: function() {
          return ismaster;
        },
        equals: function(s) {
          if (typeof s === 'string') return s === this.name;
          return s.name === this.name;
        },
        destroy: function() {}
      });
    }
  });

  // Validate the state of the final outcome
  for (var name in outcome.servers) {
    try {
      if (outcome.servers[name].electionId) {
        outcome.servers[name].electionId = new ObjectId(outcome.servers[name].electionId.$oid);
      }

      expect(state.set[name]).to.exist;
      for (var n in outcome.servers[name]) {
        if (outcome.servers[name][n]) {
          expect(state.set[name][n]).to.eql(outcome.servers[name][n]);
        }
      }
    } catch (e) {
      return callback(e);
    }
  }

  // // Check the topology type
  expect(state.topologyType).to.equal(outcome.topologyType);
  expect(state.setName).to.equal(outcome.setName);
  callback(null, null);
}
