'use strict';
var test = require('./shared').assert;
var setupDatabase = require('./shared').setupDatabase;

describe('ObjectID', function() {
  before(function() {
    return setupDatabase(this.configuration);
  });

  /**
   * @ignore
   */
  it('shouldCorrectlyGenerateObjectID', {
    metadata: {
      requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] }
    },

    // The actual test we wish to run
    test: function(done) {
      var configuration = this.configuration;
      var ObjectID = configuration.require.ObjectID;
      var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 });
      client.connect(function(err, client) {
        var db = client.db(configuration.db);
        var number_of_tests_done = 0;

        var collection = db.collection('test_object_id_generation.data');
        // Insert test documents (creates collections and test fetch by query)
        collection.insert({ name: 'Fred', age: 42 }, { w: 1 }, function(err, r) {
          test.equal(1, r.ops.length);
          test.ok(r.ops[0]['_id'].toHexString().length === 24);
          // Locate the first document inserted
          collection.findOne({ name: 'Fred' }, function(err, document) {
            test.equal(r.ops[0]['_id'].toHexString(), document._id.toHexString());
            number_of_tests_done++;
          });
        });

        // Insert another test document and collect using ObjectId
        collection.insert({ name: 'Pat', age: 21 }, { w: 1 }, function(err, r) {
          test.equal(1, r.ops.length);
          test.ok(r.ops[0]['_id'].toHexString().length === 24);
          // Locate the first document inserted
          collection.findOne(r.ops[0]['_id'], function(err, document) {
            test.equal(r.ops[0]['_id'].toHexString(), document._id.toHexString());
            number_of_tests_done++;
          });
        });

        // Manually created id
        var objectId = new ObjectID(null);
        // Insert a manually created document with generated oid
        collection.insert({ _id: objectId, name: 'Donald', age: 95 }, { w: 1 }, function(err, r) {
          test.equal(1, r.ops.length);
          test.ok(r.ops[0]['_id'].toHexString().length === 24);
          test.equal(objectId.toHexString(), r.ops[0]['_id'].toHexString());
          // Locate the first document inserted
          collection.findOne(r.ops[0]['_id'], function(err, document) {
            test.equal(r.ops[0]['_id'].toHexString(), document._id.toHexString());
            test.equal(objectId.toHexString(), document._id.toHexString());
            number_of_tests_done++;
          });
        });

        var intervalId = setInterval(function() {
          if (number_of_tests_done === 3) {
            clearInterval(intervalId);
            client.close(done);
          }
        }, 100);
      });
    }
  });

  /**
   * @ignore
   */
  it('shouldCorrectlyRetrieve24CharacterHexStringFromToString', {
    metadata: {
      requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] }
    },

    // The actual test we wish to run
    test: function(done) {
      var configuration = this.configuration;
      var ObjectID = configuration.require.ObjectID;
      // Create a new ObjectID
      var objectId = new ObjectID();
      // Verify that the hex string is 24 characters long
      test.equal(24, objectId.toString().length);
      done();
    }
  });

  /**
   * @ignore
   */
  it('shouldCorrectlyRetrieve24CharacterHexStringFromToJSON', {
    metadata: {
      requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] }
    },

    // The actual test we wish to run
    test: function(done) {
      var configuration = this.configuration;
      var ObjectID = configuration.require.ObjectID;
      // Create a new ObjectID
      var objectId = new ObjectID();
      // Verify that the hex string is 24 characters long
      test.equal(24, objectId.toJSON().length);
      done();
    }
  });

  /**
   * @ignore
   */
  it('shouldCorrectlyCreateOIDNotUsingObjectID', {
    metadata: {
      requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] }
    },

    // The actual test we wish to run
    test: function(done) {
      var configuration = this.configuration;
      var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 });
      client.connect(function(err, client) {
        var db = client.db(configuration.db);
        var collection = db.collection('test_non_oid_id');
        var date = new Date();
        date.setUTCDate(12);
        date.setUTCFullYear(2009);
        date.setUTCMonth(11 - 1);
        date.setUTCHours(12);
        date.setUTCMinutes(0);
        date.setUTCSeconds(30);

        collection.insert({ _id: date }, { w: 1 }, function(err) {
          test.equal(null, err);
          collection.find({ _id: date }).toArray(function(err, items) {
            test.equal('' + date, '' + items[0]._id);

            // Let's close the db
            client.close(done);
          });
        });
      });
    }
  });

  /**
   * @ignore
   */
  it('shouldCorrectlyGenerateObjectIDFromTimestamp', {
    metadata: {
      requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] }
    },

    // The actual test we wish to run
    test: function(done) {
      var configuration = this.configuration;
      var ObjectID = configuration.require.ObjectID;

      var timestamp = Math.floor(new Date().getTime() / 1000);
      var objectID = new ObjectID(timestamp);
      var time2 = objectID.generationTime;
      test.equal(timestamp, time2);
      done();
    }
  });

  /**
   * @ignore
   */
  it('shouldCorrectlyCreateAnObjectIDAndOverrideTheTimestamp', {
    metadata: {
      requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] }
    },

    // The actual test we wish to run
    test: function(done) {
      var configuration = this.configuration;
      var ObjectID = configuration.require.ObjectID;

      var timestamp = 1000;
      var objectID = new ObjectID();
      var id1 = objectID.id;
      // Override the timestamp
      objectID.generationTime = timestamp;
      var id2 = objectID.id;

      // Check the timestamp
      if (id1 instanceof Buffer && id2 instanceof Buffer) {
        test.deepEqual(id1.slice(0, 4), id2.slice(0, 4));
      } else {
        test.equal(id1.substr(4), id2.substr(4));
      }

      done();
    }
  });

  /**
   * @ignore
   */
  it('shouldCorrectlyInsertWithObjectId', {
    metadata: {
      requires: { topology: ['single', 'replicaset', 'sharded', 'ssl', 'heap', 'wiredtiger'] }
    },

    // The actual test we wish to run
    test: function(done) {
      var configuration = this.configuration;
      var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 });
      client.connect(function(err, client) {
        test.equal(null, err);

        var db = client.db(configuration.db);
        var collection = db.collection('shouldCorrectlyInsertWithObjectId');
        collection.insert({}, { w: 1 }, function(err) {
          test.equal(null, err);
          const firstCompareDate = new Date();

          setTimeout(function() {
            collection.insert({}, { w: 1 }, function(err) {
              test.equal(null, err);
              const secondCompareDate = new Date();

              collection.find().toArray(function(err, items) {
                test.equal(null, err);

                // Date 1
                var date1 = new Date();
                date1.setTime(items[0]._id.generationTime * 1000);
                // Date 2
                var date2 = new Date();
                date2.setTime(items[1]._id.generationTime * 1000);

                // Compare
                test.equal(firstCompareDate.getFullYear(), date1.getFullYear());
                test.equal(firstCompareDate.getDate(), date1.getDate());
                test.equal(firstCompareDate.getMonth(), date1.getMonth());
                test.equal(firstCompareDate.getHours(), date1.getHours());

                test.equal(secondCompareDate.getFullYear(), date2.getFullYear());
                test.equal(secondCompareDate.getDate(), date2.getDate());
                test.equal(secondCompareDate.getMonth(), date2.getMonth());
                test.equal(secondCompareDate.getHours(), date2.getHours());
                // Let's close the db
                client.close(done);
              });
            });
          }, 2000);
        });
      });
    }
  });
});
