'use strict';

var chai = require('chai'),
    sinon = require('sinon'),
    sinonChai = require('sinon-chai'),
    clone = require('clone'),
    ElasticsearchBulkIndexWritable = require('../');

chai.use(sinonChai);

var expect = chai.expect;

var recordFixture = require('./fixture/record.json');
var successResponseFixture = require('./fixture/success-response.json');
var errorResponseFixture = require('./fixture/error-response.json');

describe('ElastisearchBulkIndexWritable', function() {
    beforeEach(function() {
        this.sinon = sinon.sandbox.create();
    });

    afterEach(function() {
        this.sinon.restore();
    });

    describe('constructor', function() {
        it('should require client', function() {
            expect(function() {
                new ElasticsearchBulkIndexWritable();
            }).to.Throw(Error, 'client is required');
        });

        it('should default highWaterMark to 16', function() {
            var stream = new ElasticsearchBulkIndexWritable({});

            expect(stream.highWaterMark).to.eq(16);
        });
    });

    describe('queue', function() {
        beforeEach(function() {
            this.stream = new ElasticsearchBulkIndexWritable({}, { highWaterMark: 10 });
        });

        it('should queue up number of items equal to highWaterMark', function(done) {
            this.sinon.stub(this.stream, '_flush').yields();

            for (var i = 0; i < 8; i++) {
                this.stream.write(recordFixture);
            }

            this.stream.write(recordFixture, function() {
                expect(this.stream._flush).to.not.have.been.called;

                this.stream.write(recordFixture, function() {
                    expect(this.stream._flush).to.have.been.calledOnce;

                    done();
                }.bind(this));
            }.bind(this));
        });

        it('should flush queue if stream is closed', function(done) {
            this.sinon.stub(this.stream, '_flush').yields();

            this.stream.end(recordFixture, function() {
                expect(this.stream._flush).to.have.been.calledOnce;

                done();
            }.bind(this));
        });
    });

    describe('flushing', function() {
        function getMissingFieldTest(fieldName) {
            return function(done) {
                this.stream.on('error', function(error) {
                    expect(error).to.be.instanceOf(Error);
                    expect(error.message).to.eq(fieldName + ' is required');

                    done();
                });

                var fixture = clone(recordFixture);
                delete fixture[fieldName];

                this.stream.end(fixture);
            };
        }

        beforeEach(function() {
            this.client = {
                bulk: this.sinon.stub()
            };

            this.stream = new ElasticsearchBulkIndexWritable(this.client, {
                highWaterMark: 6
            });
        });

        it('should write records to elasticsearch', function(done) {
            this.client.bulk.yields(null, successResponseFixture);

            this.stream.end(recordFixture, function() {
                expect(this.client.bulk).to.have.been.called;

                done();
            }.bind(this));
        });

        it('should do nothing if there is nothing in the queue when the stream is closed', function(done) {
            this.client.bulk.yields(null, successResponseFixture);

            this.stream.on('finish', function() {
                expect(this.client.bulk).to.have.been.calledOnce;

                done();
            }.bind(this));

            for (var i = 0; i < 6; i++) {
                this.stream.write(recordFixture);
            }

            this.stream.end();
        });

        it('should trigger error on elasticsearch error', function(done) {
            this.client.bulk.yields(new Error('Fail'));

            this.stream.on('error', function(error) {
                expect(error.message).to.eq('Fail');

                done();
            });

            this.stream.end(recordFixture);
        });

        it('should trigger error on bulk errors', function(done) {
            this.client.bulk.yields(null, errorResponseFixture);

            this.stream.on('error', function(error) {
                expect(error).to.be.instanceOf(Error);
                expect(error.message).to.deep.eq('InternalServerError,Forbidden');

                done();
            });

            this.stream.write(recordFixture);
            this.stream.end(recordFixture);
        });

        it('should throw error on index missing in record', getMissingFieldTest('index'));

        it('should throw error on type missing in record', getMissingFieldTest('type'));

        it('should throw error on body missing in record', getMissingFieldTest('body'));
    });

    describe('flush timeout', function() {
        beforeEach(function() {
            this.client = {
                bulk: this.sinon.stub()
            };

            this.stream = new ElasticsearchBulkIndexWritable(this.client, {
                highWaterMark: 10,
                flushTimeout: 1000
            });
        });

        it('should flush queue if there is something in the queue after timeout', function() {
            this.client.bulk.yields(null, successResponseFixture);
            this.clock = sinon.useFakeTimers();

            function writeRecords(stream, number) {
                for (var i = 0; i < number; i++) {
                    stream.write(recordFixture);
                }
            }

            writeRecords(this.stream, 10);
            expect(this.client.bulk.callCount).to.eq(1);

            writeRecords(this.stream, 1);
            this.clock.tick(1001);
            expect(this.client.bulk.callCount).to.eq(2);
        });
    });
});

@@ -5435,64 +5435,8 @@
 );%0A%0A
-            function writeRecords(stream, number) %7B%0A    
     
@@ -5463,22 +5463,18 @@
  0; i %3C 
-number
+10
 ; i++) %7B
@@ -5490,20 +5490,21 @@
         
-   
- 
+this.
 stream.w
@@ -5540,71 +5540,10 @@
     
-    %7D%0A            %7D%0A%0A            writeRecords(this.stream, 10);
+%7D%0A
 %0A   
@@ -5613,35 +5613,39 @@
     
+this.stream.
 write
-R
+(r
 ecord
-s(this.stream, 1
+Fixture
 );%0A 

Remove superflous function in test