"use strict";
var should = require('should')
  , fs = require('fs')
  , zlib = require('zlib')
  , util = require('util')
  , async = require('async')
  , format = require('date-format')
  , streams = require('readable-stream')
  , DateRollingFileStream
  , testTime = new Date(2012, 8, 12, 10, 37, 11);

DateRollingFileStream = require('../lib').DateRollingFileStream;

function remove(filename, cb) {
  fs.unlink(filename, function () {
    cb();
  });
}

function now() {
  return testTime.getTime();
}

describe('DateRollingFileStream', function () {
  describe('arguments', function () {
    var stream;

    before(function (done) {
      stream = new DateRollingFileStream(
        __dirname + '/test-date-rolling-file-stream-1',
        'yyyy-mm-dd.hh'
      );
      done();
    });

    after(function (done) {
      remove(__dirname + '/test-date-rolling-file-stream-1', done);
    });

    it('should take a filename and a pattern and return a WritableStream', function (done) {
      stream.filename.should.eql(__dirname + '/test-date-rolling-file-stream-1');
      stream.pattern.should.eql('yyyy-mm-dd.hh');
      stream.should.be.instanceOf(streams.Writable);
      done();
    });

    it('with default settings for the underlying stream', function (done) {
      stream.theStream.mode.should.eql(420);
      stream.theStream.flags.should.eql('a');
      //encoding is not available on the underlying stream
      //assert.equal(stream.encoding, 'utf8');
      done();
    });
  });

  describe('default arguments', function () {
    var stream;

    before(function(done) {
      stream = new DateRollingFileStream(__dirname + '/test-date-rolling-file-stream-2');
      done();
    });

    after(function (done) {
      remove(__dirname + '/test-date-rolling-file-stream-2', done);
    });

    it('should have pattern of .yyyy-MM-dd', function (done) {
      stream.pattern.should.eql('.yyyy-MM-dd');
      done();
    });
  });

  describe('with stream arguments', function () {
    var stream;

    before(function(done) {
      stream = new DateRollingFileStream(
        __dirname + '/test-date-rolling-file-stream-3',
        'yyyy-MM-dd',
        {mode: parseInt('0666', 8)}
      );
      done();
    });

    after(function (done) {
      remove(__dirname + '/test-date-rolling-file-stream-3', done);
    });

    it('should pass them to the underlying stream', function (done) {
      stream.theStream.mode.should.eql(parseInt('0666', 8));
      done();
    });
  });

  describe('with stream arguments but no pattern', function () {
    var stream;

    before(function (done) {
      stream = new DateRollingFileStream(
        __dirname + '/test-date-rolling-file-stream-4',
        {mode: parseInt('0666', 8)}
      );
      done();
    });

    after(function (done) {
      remove(__dirname + '/test-date-rolling-file-stream-4', done);
    });

    it('should pass them to the underlying stream', function (done) {
      stream.theStream.mode.should.eql(parseInt('0666', 8));
      done();
    });

    it('should use default pattern', function (done) {
      stream.pattern.should.eql('.yyyy-MM-dd');
      done();
    });
  });

  describe('with a pattern of .yyyy-MM-dd', function () {
    var stream;

    before(function (done) {
      stream = new DateRollingFileStream(
        __dirname + '/test-date-rolling-file-stream-5', '.yyyy-MM-dd',
        null,
        now
      );
      stream.write("First message\n", 'utf8', done);
    });

    after(function (done) {
      remove(__dirname + '/test-date-rolling-file-stream-5', done);
    });

    it('should create a file with the base name', function (done) {
      fs.readFile(__dirname + '/test-date-rolling-file-stream-5', 'utf8', function (err, contents) {
        contents.should.eql("First message\n");
        done(err);
      });
    });

    describe('when the day changes', function () {

      before(function (done) {
        testTime = new Date(2012, 8, 13, 0, 10, 12);
        stream.write("Second message\n", 'utf8', done);
      });

      after(function (done) {
        remove(__dirname + '/test-date-rolling-file-stream-5.2012-09-12', done);
      });

      describe('the number of files', function () {
        var files = [];

        before(function (done) {
          fs.readdir(__dirname, function (err, list) {
            files = list;
            done(err);
          });
        });

        it('should be two', function (done) {
          files.filter(
            function (file) {
              return file.indexOf('test-date-rolling-file-stream-5') > -1;
            }
          ).should.have.length(2);
          done();
        });
      });

      describe('the file without a date', function () {
        it('should contain the second message', function (done) {
          fs.readFile(
            __dirname + '/test-date-rolling-file-stream-5', 'utf8',
            function (err, contents) {
              contents.should.eql("Second message\n");
              done(err);
            }
          );
        });
      });

      describe('the file with the date', function () {
        it('should contain the first message', function (done) {
          fs.readFile(
            __dirname + '/test-date-rolling-file-stream-5.2012-09-12', 'utf8',
            function (err, contents) {
              contents.should.eql("First message\n");
              done(err);
            }
          );
        });
      });
    });
  });

  describe('with alwaysIncludePattern', function () {
    var stream;

    before(function (done) {
      testTime = new Date(2012, 8, 12, 0, 10, 12);
      remove(
        __dirname + '/test-date-rolling-file-stream-pattern.2012-09-12',
        function () {
          stream = new DateRollingFileStream(
            __dirname + '/test-date-rolling-file-stream-pattern',
            '.yyyy-MM-dd',
            {alwaysIncludePattern: true},
            now
          );
          setTimeout(function() {
            stream.write("First message\n", 'utf8', done);
          }, 50);
        }
      );
    });

    after(function (done) {
      remove(__dirname + '/test-date-rolling-file-stream-pattern.2012-09-12', done);
    });

    it('should create a file with the pattern set', function (done) {
      fs.readFile(
        __dirname + '/test-date-rolling-file-stream-pattern.2012-09-12', 'utf8',
        function (err, contents) {
          contents.should.eql("First message\n");
          done(err);
        }
      );
    });

    describe('when the day changes', function () {
      before(function (done) {
        testTime = new Date(2012, 8, 13, 0, 10, 12);
        stream.write("Second message\n", 'utf8', done);
      });

      after(function (done) {
        remove(__dirname + '/test-date-rolling-file-stream-pattern.2012-09-13', done);
      });

      describe('the number of files', function () {
        it('should be two', function (done) {
          fs.readdir(__dirname, function (err, files) {
            files.filter(
              function (file) {
                return file.indexOf('test-date-rolling-file-stream-pattern') > -1;
              }
            ).should.have.length(2);
            done(err);
          });
        });
      });

      describe('the file with the later date', function () {
        it('should contain the second message', function (done) {
          fs.readFile(
            __dirname + '/test-date-rolling-file-stream-pattern.2012-09-13', 'utf8',
            function (err, contents) {
              contents.should.eql("Second message\n");
              done(err);
            }
          );
        });
      });

      describe('the file with the date', function () {
        it('should contain the first message', function (done) {
          fs.readFile(
            __dirname + '/test-date-rolling-file-stream-pattern.2012-09-12', 'utf8',
            function (err, contents) {
              contents.should.eql("First message\n");
              done(err);
            }
          );
        });
      });
    });
  });

  describe('with compress option', function () {
    var stream;

    before(function (done) {
      testTime = new Date(2012, 8, 12, 0, 10, 12);
      stream = new DateRollingFileStream(
        __dirname + '/compressed.log',
        '.yyyy-MM-dd',
        {compress: true},
        now
      );
      stream.write("First message\n", 'utf8', done);
    });

    describe('when the day changes', function () {
      before(function (done) {
        testTime = new Date(2012, 8, 13, 0, 10, 12);
        stream.write("Second message\n", 'utf8', done);
      });

      it('should be two files, one compressed', function (done) {
        fs.readdir(__dirname, function (err, files) {
          var logFiles = files.filter(
            function (file) {
              return file.indexOf('compressed.log') > -1;
            }
          );
          logFiles.should.have.length(2);

          zlib.gunzip(
            fs.readFileSync(__dirname + '/compressed.log.2012-09-12.gz'),
            function (err, contents) {
              contents.toString('utf8').should.eql('First message\n');
              fs.readFileSync(__dirname + '/compressed.log', 'utf8').should.eql('Second message\n');
              done(err);
            }
          );
        });
      });
    });

    after(function (done) {
      remove(
        __dirname + '/compressed.log',
        function () {
          remove(__dirname + '/compressed.log.2012-09-12.gz', done);
        }
      );
    });

  });

  describe('with keepFileExt option', function () {
    var stream;

    before(function (done) {
      testTime = new Date(2012, 8, 12, 0, 10, 12);
      stream = new DateRollingFileStream(
        __dirname + '/keepFileExt.log',
        '.yyyy-MM-dd',
        {keepFileExt: true},
        now
      );
      stream.write("First message\n", 'utf8', done);
    });

    describe('when the day changes', function () {
      before(function (done) {
        testTime = new Date(2012, 8, 13, 0, 10, 12);
        stream.write("Second message\n", 'utf8', done);
      });

      it('should be two files', function (done) {
        fs.readdir(__dirname, function (err, files) {
          var logFiles = files.filter(
            function (file) {
              return file.indexOf('keepFileExt') > -1;
            }
          );
          logFiles.should.have.length(2);
          fs.readFileSync(__dirname + '/keepFileExt.2012-09-12.log', 'utf8')
            .should.eql('First message\n');
          fs.readFileSync(__dirname + '/keepFileExt.log', 'utf8')
            .should.eql('Second message\n');
          done(err);
        });
      });
    });

    after(function (done) {
      remove(
        __dirname + '/keepFileExt.log',
        function () {
          remove(__dirname + '/keepFileExt.2012-09-12.log', done);
        }
      );
    });

  });

  describe('with compress option and keepFileExt option', function () {
    var stream;

    before(function (done) {
      testTime = new Date(2012, 8, 12, 0, 10, 12);
      stream = new DateRollingFileStream(
        __dirname + '/compressedAndKeepExt.log',
        '.yyyy-MM-dd',
        {compress: true, keepFileExt: true},
        now
      );
      stream.write("First message\n", 'utf8', done);
    });

    describe('when the day changes', function () {
      before(function (done) {
        testTime = new Date(2012, 8, 13, 0, 10, 12);
        stream.write("Second message\n", 'utf8', done);
      });

      it('should be two files, one compressed', function (done) {
        fs.readdir(__dirname, function (err, files) {
          var logFiles = files.filter(
            function (file) {
              return file.indexOf('compressedAndKeepExt') > -1;
            }
          );
          logFiles.should.have.length(2);

          zlib.gunzip(
            fs.readFileSync(__dirname + '/compressedAndKeepExt.2012-09-12.log.gz'),
            function (err, contents) {
              contents.toString('utf8').should.eql('First message\n');
              fs.readFileSync(__dirname + '/compressedAndKeepExt.log', 'utf8')
                .should.eql('Second message\n');
              done(err);
            }
          );
        });
      });
    });

    after(function (done) {
      remove(
        __dirname + '/compressedAndKeepExt.log',
        function () {
          remove(__dirname + '/compressedAndKeepExt.log.2012-09-12.gz', done);
        }
      );
    });

  });

  describe('with daysToKeep option', function () {
    var stream;
    var daysToKeep = 4;
    var numOriginalLogs = 10;

    before(function (done) {
      var day = 0;
      var streams = [];
      async.whilst(
        function () {
          return day < numOriginalLogs;
        },
        function (nextCallback) {
          testTime = new Date(2012, 8, 20 - day, 0, 10, 12);
          var currentStream = new DateRollingFileStream(
            __dirname + '/daysToKeep.log',
            '.yyyy-MM-dd',
            {
              alwaysIncludePattern: true,
              daysToKeep: daysToKeep
            },
            now
          );
          async.waterfall([
              function (callback) {
                currentStream.write(util.format("Message on day %d\n", day), 'utf8', callback);
              },
              function (callback) {
                fs.utimes(currentStream.filename, testTime, testTime, callback);
              }
            ],
            function (err) {
              day++;
              streams.push(currentStream);
              nextCallback(err);
            });
        },
        function (err, n) {
          stream = streams[0];
          done(err);
        });

      describe('when the day changes', function () {
        before(function (done) {
          testTime = new Date(2012, 8, 21, 0, 10, 12);
          stream.write("Second message\n", 'utf8', done);
        });

        it('should be daysToKeep + 1 files left from numOriginalLogs', function (done) {
          fs.readdir(__dirname, function (err, files) {
            var logFiles = files.filter(
              function (file) {
                return file.indexOf('daysToKeep.log') > -1;
              }
            );
            logFiles.should.have.length(daysToKeep + 1);
            done(err);
          });
        });
      });

      after(function (done) {
        fs.readdir(__dirname, function (err, files) {
          var logFiles = files.filter(
            function (file) {
              return file.indexOf('daysToKeep.log') > -1;
            }
          );
          async.each(logFiles, function (logFile, nextCallback) {
              remove(__dirname + "/" + logFile, nextCallback);
            },
            function (err) {
              done(err);
            });
        });
      });
    });
  });

  describe('with daysToKeep and compress options', function () {
    var stream;
    var daysToKeep = 4;
    var numOriginalLogs = 10;

    before(function (done) {
      var day = 0;
      var streams = [];
      async.whilst(
        function () {
          return day < numOriginalLogs;
        },
        function (nextCallback) {
          testTime = new Date(2012, 8, 20 - day, 0, 10, 12);
          var currentStream = new DateRollingFileStream(
            __dirname + '/compressedDaysToKeep.log',
            '.yyyy-MM-dd',
            {
              alwaysIncludePattern: true,
              compress: true,
              daysToKeep: daysToKeep
            },
            now
          );
          async.waterfall([
              function (callback) {
                currentStream.write(util.format("Message on day %d\n", day), 'utf8', callback);
              },
              function (callback) {
                currentStream.compress(currentStream.filename, callback);
              },
              function (callback) {
                fs.utimes(currentStream.filename + ".gz", testTime, testTime, callback);
              }
            ],
            function (err) {
              day++;
              streams.push(currentStream);
              nextCallback(err);
            });
        },
        function (err, n) {

          // Uncompress the most recent stream which will be the one we roll over
          // for testing
          stream = streams[0];
          var compressedFilename = stream.filename + '.gz';
          var gzip = zlib.createGzip();
          var inp = fs.createReadStream(compressedFilename);
          var out = fs.createWriteStream(stream.filename);
          inp.pipe(gzip).pipe(out);

          out.on('finish', function (err) {
            fs.unlink(compressedFilename, done);
          });
        });
    });

    describe('when the day changes', function () {
      before(function (done) {
        testTime = new Date(2012, 8, 21, 0, 10, 12);
        stream.write("New file message\n", 'utf8', done);
      });

      it('should be 4 files left from original 3', function (done) {
        fs.readdir(__dirname, function (err, files) {
          var logFiles = files.filter(
            function (file) {
              return file.indexOf('compressedDaysToKeep.log') > -1;
            }
          );
          logFiles.should.have.length(daysToKeep + 1);
          done(err);
        });
      });
    });

    after(function (done) {
      fs.readdir(__dirname, function (err, files) {
        var logFiles = files.filter(
          function (file) {
            return file.indexOf('compressedDaysToKeep.log') > -1;
          }
        );
        async.each(logFiles, function (logFile, nextCallback) {
            remove(__dirname + "/" + logFile, nextCallback);
          },
          function (err) {
            done(err);
          });
      });
    });
  });
});
