'use strict';

var adapters = ['http', 'local'];

adapters.forEach(function (adapter) {
  describe('test.conflicts.js-' + adapter, function () {

    var dbs = {};

    beforeEach(function () {
      dbs.name = testUtils.adapterUrl(adapter, 'testdb');
    });

    afterEach(function (done) {
      testUtils.cleanup([dbs.name], done);
    });

    it('Testing conflicts', function (done) {
      var db = new PouchDB(dbs.name);
      var doc = {_id: 'foo', a: 1, b: 1};
      db.put(doc, function (err, res) {
        doc._rev = res.rev;
        should.exist(res.ok, 'Put first document');
        db.get('foo', function (err, doc2) {
          doc._id.should.equal(doc2._id);
          doc.should.have.property('_rev');
          doc2.should.have.property('_rev');
          doc.a = 2;
          doc2.a = 3;
          db.put(doc, function (err, res) {
            should.exist(res.ok, 'Put second doc');
            db.put(doc2, function (err) {
              err.name.should.equal('conflict', 'Put got a conflicts');
              db.changes({return_docs: true}).on('complete', function (results) {
                results.results.should.have.length(1);
                doc2._rev = undefined;
                db.put(doc2, function (err) {
                  err.name.should.equal('conflict', 'Another conflict');
                  done();
                });
              }).on('error', done);
            });
          });
        });
      });
    });

    it('Testing conflicts', function (done) {
      var doc = {_id: 'fubar', a: 1, b: 1};
      var db = new PouchDB(dbs.name);
      db.put(doc, function (err, ndoc) {
        doc._rev = ndoc.rev;
        db.remove(doc, function () {
          delete doc._rev;
          db.put(doc, function (err, ndoc) {
            if (err) {
              return done(err);
            }
            should.exist(ndoc.ok, 'written previously deleted doc without rev');
            done();
          });
        });
      });
    });

    it('force put ok on 1st level', function () {
      var db = new PouchDB(dbs.name);
      var docId = "docId";
      var rev1, rev2, rev3, rev2_;
      // given
      return db.put({_id: docId, update:1}).then(function (result) {
        rev1 = result.rev;
        return db.put({_id: docId, update:2.1, _rev: rev1});
      }).then(function (result) {
        rev2 = result.rev;
        return db.put({_id: docId, update:3, _rev:rev2});
      })
      // when
      .then(function (result) {
        rev3 = result.rev;
        return db.put({_id: docId, update:2.2, _rev: rev1}, {force: true});
      })
      // then
      .then(function (result) {
        rev2_ = result.rev;
        rev2_.should.not.equal(rev3);
        rev2_.substring(0, 2).should.equal('2-');
        should.exist(result.ok, 'update based on nonleaf revision');

        return db.get(docId, {conflicts: true, revs: true});
      }).then(function (doc) {
        doc._rev.should.equal(rev3);
        doc._conflicts.should.eql([rev2_]);

        return db.get(docId, {conflicts: true, revs: true, rev: rev2_});
      });
    });

    it('force put ok on 2nd level', function () {
      var db = new PouchDB(dbs.name);
      var docId = "docId";
      var rev2, rev3, rev4, rev3_;
      // given
      return db.put({_id: docId, update: 1}).then(function (result) {
        return db.put({_id: docId, update: 2, _rev: result.rev});
      }).then(function (result) {
        rev2 = result.rev;
        return db.put({_id: docId, update: 3.1, _rev: rev2});
      }).then(function (result) {
        rev3 = result.rev;
        return db.put({_id: docId, update: 4, _rev: rev3});
      })
      // when
      .then(function (result) {
        rev4 = result.rev;
        return db.put({_id: docId, update:3.2, _rev: rev2}, {force: true});
      })
      // then
      .then(function (result) {
        rev3_ = result.rev;
        rev3_.should.not.equal(rev4);
        rev3_.substring(0, 2).should.equal('3-');
        should.exist(result.ok, 'update based on nonleaf revision');

        return db.get(docId, {conflicts: true, revs: true});
      }).then(function (doc) {
        doc._rev.should.equal(rev4);
        doc._conflicts.should.eql([rev3_]);

        return db.get(docId, {conflicts: true, revs: true, rev: rev3_});
      });
    });

    // Each revision includes a list of previous revisions. The
    // revision with the longest revision history list becomes the
    // winning revision. If they are the same, the _rev values are
    // compared in ASCII sort order, and the highest wins. So, in our
    // example, 2-de0ea16f8621cbac506d23a0fbbde08a beats
    // 2-7c971bb974251ae8541b8fe045964219.

    it('Conflict resolution 1', function () {
      var docs = [
        {
          _id: 'fubar',
          _rev: '1-a',
          _revisions: {
            start: 1,
            ids: [ 'a' ]
          }
        }, {
          _id: 'fubar',
          _rev: '1-b',
          _revisions: {
            start: 1,
            ids: [ 'b' ]
          }
        }, {
          _id: 'fubar',
          _rev: '1-1',
          _revisions: {
            start: 1,
            ids: [ '1' ]
          }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('1-b', 'Correct revision wins');
        return db.bulkDocs({
          new_edits: false,
          docs: [{
            _id: 'fubar',
            _rev: '2-2',
            _revisions: {
              start: 2,
              ids: [ '2', '1' ]
            }
          }]
        });
      }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('2-2', 'Correct revision wins');
      });
    });

    it('Conflict resolution 2', function () {
      var docs = [
        {
          _id: 'fubar',
          _rev: '2-a',
          _revisions: {
            start: 2,
            ids: [ 'a' ]
          }
        }, {
          _id: 'fubar',
          _rev: '1-b',
          _revisions: {
            start: 1,
            ids: [ 'b' ]
          }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('2-a', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('Conflict resolution 3', function () {
      var docs = [
        {
          _id: 'fubar',
          _rev: '10-a',
          _revisions: {
            start: 10,
            ids: [ 'a' ]
          }
        }, {
          _id: 'fubar',
          _rev: '2-b',
          _revisions: {
            start: 2,
            ids: [ 'b' ]
          }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('10-a', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('Conflict resolution 4-a', function () {
      var docs = [
        {
          _id: 'fubar',
          _rev: '1-a1',
          _revisions: { start: 1, ids: [ 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _deleted: true,
          _rev: '3-a3',
          _revisions: { start: 3, ids: [ 'a3', 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '1-b1',
          _revisions: { start: 1, ids: [ 'b1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('1-b1', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('Conflict resolution 4-b', function () {
      var docs = [
        {
          _id: 'fubar',
          _deleted: true,
          _rev: '3-a3',
          _revisions: { start: 3, ids: [ 'a3', 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '1-a1',
          _revisions: { start: 1, ids: [ 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '1-b1',
          _revisions: { start: 1, ids: [ 'b1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('1-b1', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('Conflict resolution 4-c', function () {
      var docs = [
        {
          _id: 'fubar',
          _rev: '1-a1',
          _revisions: { start: 1, ids: [ 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '1-b1',
          _revisions: { start: 1, ids: [ 'b1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _deleted: true,
          _rev: '3-a3',
          _revisions: { start: 3, ids: [ 'a3', 'a2', 'a1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
          doc._rev.should.equal('1-b1', 'Correct revision wins');
          return db.info();
        }).then(function (info) {
          info.doc_count.should.equal(1, 'Correct number of docs');
        });
    });

    it('Conflict resolution 4-d', function () {
      var docs = [
        {
          _id: 'fubar',
          _rev: '1-a1',
          _revisions: { start: 1, ids: [ 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '1-b1',
          _revisions: { start: 1, ids: [ 'b1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _deleted: true,
          _rev: '3-a3',
          _revisions: { start: 3, ids: [ 'a3', 'a2', 'a1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('1-b1', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('Conflict resolution 4-e', function () {
      var docs = [
        {
          _id: 'fubar',
          _deleted: true,
          _rev: '3-a3',
          _revisions: { start: 3, ids: [ 'a3', 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '1-b1',
          _revisions: { start: 1, ids: [ 'b1' ] }
        }, {
          _id: 'fubar',
          _rev: '1-a1',
          _revisions: { start: 1, ids: [ 'a1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('1-b1', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('Conflict resolution 5-a', function () {
      var docs = [
        {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _deleted: true,
          _rev: '1-b1',
          _revisions: { start: 1, ids: [ 'b1' ] }
        }, {
          _id: 'fubar',
          _deleted: true,
          _rev: '1-c1',
          _revisions: { start: 1, ids: [ 'c1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('2-a2', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('Conflict resolution 5-b', function () {
      var docs = [
        {
          _id: 'fubar',
          _deleted: true,
          _rev: '1-b1',
          _revisions: { start: 1, ids: [ 'b1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _deleted: true,
          _rev: '1-c1',
          _revisions: { start: 1, ids: [ 'c1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('2-a2', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('Conflict resolution 5-c', function () {
      var docs = [
        {
          _id: 'fubar',
          _deleted: true,
          _rev: '1-b1',
          _revisions: { start: 1, ids: [ 'b1' ] }
        }, {
          _id: 'fubar',
          _deleted: true,
          _rev: '1-c1',
          _revisions: { start: 1, ids: [ 'c1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({ docs, new_edits: false }).then(function () {
        return db.get('fubar');
      }).then(function (doc) {
        doc._rev.should.equal('2-a2', 'Correct revision wins');
        return db.info();
      }).then(function (info) {
        info.doc_count.should.equal(1, 'Correct number of docs');
      });
    });

    it('#2543 excessive recursion with merging', function () {
      var chain = Promise.resolve();

      var db = new PouchDB(dbs.name);

      function addTask(batch) {
        return function () {
          var docs = [];
          for (var i = 0; i < 50; i++) {
            var hash = batch + 'a' +  i;
            docs.push({
              _id: 'foo',
              _rev: '2-' + hash,
              _revisions: {
                start: 2,
                ids: [hash, 'a']
              }
            });
          }
          return db.bulkDocs(docs, {new_edits: false});
        };
      }

      chain = chain.then(function () {
        return db.bulkDocs([{
          _id: 'foo',
          _rev: '1-a'
        }], {new_edits: false});
      });

      for (var i = 0; i < 10; i++) {
        chain = chain.then(addTask(i));
      }
      return chain;
    });

    it('5832 - update losing leaf returns correct rev', function () {
      // given
      var docs = [
        {
          _id: 'fubar',
          _rev: '1-a1',
          _revisions: { start: 1, ids: [ 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-a2',
          _revisions: { start: 2, ids: [ 'a2', 'a1' ] }
        }, {
          _id: 'fubar',
          _rev: '2-b2',
          _revisions: { start: 2, ids: [ 'b2', 'a1' ] }
        }
      ];
      var db = new PouchDB(dbs.name);
      return db.bulkDocs({
        docs, new_edits: false
      }).then(function () {
        return db.get('fubar', { conflicts: true });
      })
      .then(function (doc) {
        return db.remove(doc);
      })
      .then(function (result) {
        result.rev[0].should.equal('3');
      });
    });

  });
});
