"use strict";
var vows = require('vows')
, assert = require('assert')
, sandbox = require('sandboxed-module')
, log4js = require('../lib/log4js')
, realLayouts = require('../lib/layouts')
, setupLogging = function(options, category, compressedLength) {
  var fakeDgram = {
    sent: false,
    socket: {
      packetLength: 0,
      closed: false,
      close: function() {
        this.closed = true;
      },
      send: function(pkt, offset, pktLength, port, host) {
        fakeDgram.sent = true;
        this.packet = pkt;
        this.offset = offset;
        this.packetLength = pktLength;
        this.port = port;
        this.host = host;
      }
    },
    createSocket: function(type) {
      this.type = type;
      return this.socket;
    }
  }
  , fakeZlib = {
    gzip: function(objectToCompress, callback) {
      fakeZlib.uncompressed = objectToCompress;
      if (this.shouldError) {
        callback({ stack: "oh noes" });
        return;
      }

      if (compressedLength) {
        callback(null, { length: compressedLength });
      } else {
        callback(null, "I've been compressed");
      }
    }
  }
  , exitHandler
  , fakeConsole = {
    error: function(message) {
      this.message = message;
    }
  }
  , fakeLayouts = {
    layout: function(type, options) {
      this.type = type;
      this.options = options;
      return realLayouts.messagePassThroughLayout;
    },
    messagePassThroughLayout: realLayouts.messagePassThroughLayout
  }
  , appender = sandbox.require('../lib/appenders/gelf', {
    requires: {
      dgram: fakeDgram,
      zlib: fakeZlib,
      '../layouts': fakeLayouts
    },
    globals: {
      process: {
        on: function(evt, handler) {
          if (evt === 'exit') {
            exitHandler = handler;
          }
        }
      },
      console: fakeConsole
    }
  });

  log4js.clearAppenders();
  log4js.addAppender(appender.configure(options || {}), category || "gelf-test");
  return {
    dgram: fakeDgram,
    compress: fakeZlib,
    exitHandler: exitHandler,
    console: fakeConsole,
    layouts: fakeLayouts,
    logger: log4js.getLogger(category || "gelf-test")
  };
};

vows.describe('log4js gelfAppender').addBatch({

  'with default gelfAppender settings': {
    topic: function() {
      var setup = setupLogging();
      setup.logger.info("This is a test");
      return setup;
    },
    'the dgram packet': {
      topic: function(setup) {
        return setup.dgram;
      },
      'should be sent via udp to the localhost gelf server': function(dgram) {
        assert.equal(dgram.type, "udp4");
        assert.equal(dgram.socket.host, "localhost");
        assert.equal(dgram.socket.port, 12201);
        assert.equal(dgram.socket.offset, 0);
        assert.ok(dgram.socket.packetLength > 0, "Received blank message");
      },
      'should be compressed': function(dgram) {
        assert.equal(dgram.socket.packet, "I've been compressed");
      }
    },
    'the uncompressed log message': {
      topic: function(setup) {
        var message = JSON.parse(setup.compress.uncompressed);
        return message;
      },
      'should be in the gelf format': function(message) {
        assert.equal(message.version, '1.1');
        assert.equal(message.host, require('os').hostname());
        assert.equal(message.level, 6); //INFO
        assert.equal(message.short_message, 'This is a test');
      }
    }
  },
  'with a message longer than 8k': {
    topic: function() {
      var setup = setupLogging(undefined, undefined, 10240);
      setup.logger.info("Blah.");
      return setup;
    },
    'the dgram packet': {
      topic: function(setup) {
        return setup.dgram;
      },
      'should not be sent': function(dgram) {
        assert.equal(dgram.sent, false);
      }
    }
  },
  'with non-default options': {
    topic: function() {
      var setup = setupLogging({
        host: 'somewhere',
        port: 12345,
        hostname: 'cheese',
        facility: 'nonsense'
      });
      setup.logger.debug("Just testing.");
      return setup;
    },
    'the dgram packet': {
      topic: function(setup) {
        return setup.dgram;
      },
      'should pick up the options': function(dgram) {
        assert.equal(dgram.socket.host, 'somewhere');
        assert.equal(dgram.socket.port, 12345);
      }
    },
    'the uncompressed packet': {
      topic: function(setup) {
        var message = JSON.parse(setup.compress.uncompressed);
        return message;
      },
      'should pick up the options': function(message) {
        assert.equal(message.host, 'cheese');
        assert.equal(message._facility, 'nonsense');
      }
    }
  },

  'on process.exit': {
    topic: function() {
      var setup = setupLogging();
      setup.exitHandler();
      return setup;
    },
    'should close open sockets': function(setup) {
      assert.isTrue(setup.dgram.socket.closed);
    }
  },

  'on zlib error': {
    topic: function() {
      var setup = setupLogging();
      setup.compress.shouldError = true;
      setup.logger.info('whatever');
      return setup;
    },
    'should output to console.error': function(setup) {
      assert.equal(setup.console.message, 'oh noes');
    }
  },

  'with layout in configuration': {
    topic: function() {
      var setup = setupLogging({
        layout: {
          type: 'madeuplayout',
          earlgrey: 'yes, please'
        }
      });
      return setup;
    },
    'should pass options to layout': function(setup) {
      assert.equal(setup.layouts.type, 'madeuplayout');
      assert.equal(setup.layouts.options.earlgrey, 'yes, please');
    }
  },

  'with custom fields options': {
    topic: function() {
      var setup = setupLogging({
        host: 'somewhere',
        port: 12345,
        hostname: 'cheese',
        facility: 'nonsense',
        customFields: {
          _every1: 'Hello every one',
          _every2: 'Hello every two'
        }
      });
      var myFields = {
        GELF: true,
        _every2: 'Overwritten!',
        _myField: 'This is my field!'
      };
      setup.logger.debug(myFields, "Just testing.");
      return setup;
    },
    'the dgram packet': {
      topic: function(setup) {
        return setup.dgram;
      },
      'should pick up the options': function(dgram) {
        assert.equal(dgram.socket.host, 'somewhere');
        assert.equal(dgram.socket.port, 12345);
      }
    },
    'the uncompressed packet': {
      topic: function(setup) {
        var message = JSON.parse(setup.compress.uncompressed);
        return message;
      },
      'should pick up the options': function(message) {
        assert.equal(message.host, 'cheese');
        assert.isUndefined(message.GELF); // make sure flag was removed
        assert.equal(message._facility, 'nonsense');
        assert.equal(message._every1, 'Hello every one'); // the default value
        assert.equal(message._every2, 'Overwritten!'); // the overwritten value
        assert.equal(message._myField, 'This is my field!'); // the value for this message only
        assert.equal(message.short_message, 'Just testing.'); // skip the field object
      }
    }
  }

}).export(module);
