/* global it, describe, beforeEach, afterEach */
const os = require('os')
const path = require('path')
const fs = require('fs')
const LOG = require('./index')
const hostname = os.hostname()
function getDate () {
  var tzoffset = (new Date()).getTimezoneOffset() * 60000 // offset in milliseconds
  var localISOTime = (new Date(Date.now() - tzoffset)).toISOString().slice(0, -1)
  return localISOTime.split('.')[0].trim()
}
const manualhostname = 'testhost'
const name = 'testapp'
let file = 'testfile'
const filePath = '/tmp'

function createLogLine(level, msg) {
  return getDate() + '\t' + hostname + '\t@general/log\t' + level + '\t' + msg
}


describe('@general/log', () => {
  describe('Contructor', () => {
    it('should log default with no options', () => {
      let log = new LOG();
      expect(log.loglevel).toBe(2)
      expect(log.delimeter).toBe(' ')
      expect(log.name).toBe('@general/log')
      expect(log.hostname).toBe(hostname)
      expect(log.format).toBe('{timestamp}\t{hostname}\t{name}\t{loglevel}\t{message}')
    })
    it('should use option hostname', () => {
      let log = new LOG({
        hostname: 'test'
      });
      expect(log.loglevel).toBe(2)
      expect(log.delimeter).toBe(' ')
      expect(log.name).toBe('@general/log')
      expect(log.hostname).toBe('test')
      expect(log.format).toBe('{timestamp}\t{hostname}\t{name}\t{loglevel}\t{message}')
    })
    it('should use option name', () => {
      let log = new LOG({
        name: 'test'
      });
      expect(log.loglevel).toBe(2)
      expect(log.delimeter).toBe(' ')
      expect(log.name).toBe('test')
      expect(log.hostname).toBe(hostname)
      expect(log.format).toBe('{timestamp}\t{hostname}\t{name}\t{loglevel}\t{message}')
    })
    it('should use option delimeter', () => {
      let log = new LOG({
        delimeter: ';'
      });
      expect(log.loglevel).toBe(2)
      expect(log.delimeter).toBe(';')
      expect(log.name).toBe('@general/log')
      expect(log.hostname).toBe(hostname)
      expect(log.format).toBe('{timestamp}\t{hostname}\t{name}\t{loglevel}\t{message}')
    })
    it('should use option loglevel', () => {
      let log = new LOG({
        loglevel: 'FATAL'
      });
      expect(log.loglevel).toBe(0)
      expect(log.delimeter).toBe(' ')
      expect(log.name).toBe('@general/log')
      expect(log.hostname).toBe(hostname)
      expect(log.format).toBe('{timestamp}\t{hostname}\t{name}\t{loglevel}\t{message}')
    })
    it('should use option format', () => {
      let log = new LOG({
        format: '{message}'
      });
      expect(log.loglevel).toBe(2)
      expect(log.delimeter).toBe(' ')
      expect(log.name).toBe('@general/log')
      expect(log.hostname).toBe(hostname)
      expect(log.format).toBe('{message}')
    })
  })
  describe('log by level', () => {
    it('should log fatal only if level is appropriate', () => {
      let log = new LOG({loglevel: 'FATAL'});
      let logged = log.fatal('test')
      expect(logged).toBe(createLogLine('FATAL', 'test'))

      log = new LOG({loglevel: 'ERROR'});
      logged = log.fatal('test')
      expect(logged).toBe(createLogLine('FATAL', 'test'))

      log = new LOG({loglevel: 'WARN'});
      logged = log.fatal('test')
      expect(logged).toBe(createLogLine('FATAL', 'test'))

      log = new LOG({loglevel: 'NOTICE'});
      logged = log.fatal('test')
      expect(logged).toBe(createLogLine('FATAL', 'test'))

      log = new LOG({loglevel: 'INFO'});
      logged = log.fatal('test')
      expect(logged).toBe(createLogLine('FATAL', 'test'))

      log = new LOG({loglevel: 'DEBUG'});
      logged = log.fatal('test')
      expect(logged).toBe(createLogLine('FATAL', 'test'))
    })
    it('should log error only if level is appropriate', () => {
      let log = new LOG({loglevel: 'FATAL'});
      let logged = log.error('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'ERROR'});
      logged = log.error('test')
      expect(logged).toBe(createLogLine('ERROR', 'test'))

      log = new LOG({loglevel: 'WARN'});
      logged = log.error('test')
      expect(logged).toBe(createLogLine('ERROR', 'test'))

      log = new LOG({loglevel: 'NOTICE'});
      logged = log.error('test')
      expect(logged).toBe(createLogLine('ERROR', 'test'))

      log = new LOG({loglevel: 'INFO'});
      logged = log.error('test')
      expect(logged).toBe(createLogLine('ERROR', 'test'))

      log = new LOG({loglevel: 'DEBUG'});
      logged = log.error('test')
      expect(logged).toBe(createLogLine('ERROR', 'test'))
    })
    it('should log warnings only if level is appropriate', () => {
      let log = new LOG({loglevel: 'FATAL'});
      let logged = log.warn('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'ERROR'});
      logged = log.warn('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'WARN'});
      logged = log.warn('test')
      expect(logged).toBe(createLogLine('WARN', 'test'))

      log = new LOG({loglevel: 'NOTICE'});
      logged = log.warn('test')
      expect(logged).toBe(createLogLine('WARN', 'test'))

      log = new LOG({loglevel: 'INFO'});
      logged = log.warn('test')
      expect(logged).toBe(createLogLine('WARN', 'test'))

      log = new LOG({loglevel: 'DEBUG'});
      logged = log.warn('test')
      expect(logged).toBe(createLogLine('WARN', 'test'))
    })
    it('should log notices only if level is appropriate', () => {
      let log = new LOG({loglevel: 'FATAL'});
      let logged = log.notice('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'ERROR'});
      logged = log.notice('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'WARN'});
      logged = log.notice('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'NOTICE'});
      logged = log.notice('test')
      expect(logged).toBe(createLogLine('NOTICE', 'test'))

      log = new LOG({loglevel: 'INFO'});
      logged = log.notice('test')
      expect(logged).toBe(createLogLine('NOTICE', 'test'))

      log = new LOG({loglevel: 'DEBUG'});
      logged = log.notice('test')
      expect(logged).toBe(createLogLine('NOTICE', 'test'))
    })
    it('should log info only if level is appropriate', () => {
      let log = new LOG({loglevel: 'FATAL'});
      let logged = log.info('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'ERROR'});
      logged = log.info('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'WARN'});
      logged = log.info('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'NOTICE'});
      logged = log.info('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'INFO'});
      logged = log.info('test')
      expect(logged).toBe(createLogLine('INFO', 'test'))

      log = new LOG({loglevel: 'DEBUG'});
      logged = log.info('test')
      expect(logged).toBe(createLogLine('INFO', 'test'))
    })
    it('should log debug only if level is appropriate', () => {
      let log = new LOG({loglevel: 'FATAL'});
      let logged = log.debug('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'ERROR'});
      logged = log.debug('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'WARN'});
      logged = log.debug('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'NOTICE'});
      logged = log.debug('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'INFO'});
      logged = log.debug('test')
      expect(logged).toBe('')

      log = new LOG({loglevel: 'DEBUG'});
      logged = log.debug('test')
      expect(logged).toBe(createLogLine('DEBUG', 'test'))
    })
  })
  describe('log messages', () => {
    it('should log a string as is', () => {
      let log = new LOG({loglevel: 'DEBUG'});
      let logged = log.debug('test')
      let expected = getDate() + '\t' + hostname + '\t@general/log\tDEBUG\ttest'
      expect(logged).toBe(expected)
    })
    it('should log multiple strings with the delimeter', () => {
      let log = new LOG({loglevel: 'DEBUG'});
      let logged = log.debug('test', 'test', 'test')
      let expected = getDate() + '\t' + hostname + '\t@general/log\tDEBUG\ttest test test'
      expect(logged).toBe(expected)
    })
    it('should log a number as a string', () => {
      let log = new LOG({loglevel: 'DEBUG'});
      let logged = log.debug(8)
      let expected = getDate() + '\t' + hostname + '\t@general/log\tDEBUG\t8'
      expect(logged).toBe(expected)
    })
    it('should log a float as a string', () => {
      let log = new LOG({loglevel: 'DEBUG'});
      let logged = log.debug(8.23)
      let expected = getDate() + '\t' + hostname + '\t@general/log\tDEBUG\t8.23'
      expect(logged).toBe(expected)
    })
    it('should log a boolean as a string', () => {
      let log = new LOG({loglevel: 'DEBUG'});
      let logged = log.debug(true)
      let expected = getDate() + '\t' + hostname + '\t@general/log\tDEBUG\ttrue'
      expect(logged).toBe(expected)
    })
    it('should log an array as a string', () => {
      let log = new LOG({loglevel: 'DEBUG'});
      let logged = log.debug(['test', 'test'])
      let expected = getDate() + '\t' + hostname + '\t@general/log\tDEBUG\ttest,test'
      expect(logged).toBe(expected)
    })
    it('should log a json-object as a string', () => {
      let log = new LOG({loglevel: 'DEBUG'});
      let logged = log.debug({some: 'value'})
      let expected = getDate() + '\t' + hostname + '\t@general/log\tDEBUG\t{"some":"value"}'
      expect(logged).toBe(expected)
    })
    it('should log a mixed entry as a string', () => {
      let log = new LOG({loglevel: 'DEBUG'});
      let logged = log.debug('test', 7, 3.45, false, ['value', 'more'], {some: 'value'})
      let expected = getDate() + '\t' + hostname + '\t@general/log\tDEBUG\ttest 7 3.45 false value,more {"some":"value"}'
      expect(logged).toBe(expected)
    })
  })
  describe('log formatting', () => {
    it('should log message only if set', () => {
      let log = new LOG({
        format: '{message}',
        loglevel: 'DEBUG'
      });
      let logged = log.debug('test')
      let expected = 'test'
      expect(logged).toBe(expected)
    })
    it('should log hostname only if set', () => {
      let log = new LOG({
        format: '{hostname}',
        loglevel: 'DEBUG'
      });
      let logged = log.debug('test')
      let expected = hostname
      expect(logged).toBe(expected)
    })
    it('should log name only if set', () => {
      let log = new LOG({
        format: '{name}',
        loglevel: 'DEBUG'
      });
      let logged = log.debug('test')
      let expected = '@general/log'
      expect(logged).toBe(expected)
    })
    it('should log timestamp only if set', () => {
      let log = new LOG({
        format: '{timestamp}',
        loglevel: 'DEBUG'
      });
      let logged = log.debug('test')
      let expected = getDate()
      expect(logged).toBe(expected)
    })
    it('should log loglevel only if set', () => {
      let log = new LOG({
        format: '{loglevel}',
        loglevel: 'DEBUG'
      });
      let logged = log.debug('test')
      let expected = 'DEBUG'
      expect(logged).toBe(expected)
    })
    it('should log json like message if set', () => {
      let log = new LOG({
        format: '{"timestamp":"{timestamp}", "level":"{loglevel}", "message":"{message}"}',
        loglevel: 'DEBUG'
      });
      let logged = log.debug('test')
      let expected = '{"timestamp":"' + getDate() + '", "level":"DEBUG", "message":"test"}'
      expect(logged).toBe(expected)
    })
  })
  describe('log to file', () => {
    it('should create the folder if not exists', () => {
      let logPath = os.tmpdir() + '/log-test' + Math.floor(Math.random() * 1000)
      expect(() => {
        fs.accessSync(logPath, fs.constants.R_OK | fs.constants.W_OK)
      }).toThrow();
      let log = new LOG({
        path: logPath,
        file: 'test.log'
      });
      expect(fs.accessSync(logPath, fs.constants.R_OK | fs.constants.W_OK)).toBeUndefined()
    })
    it('should create the file if not exists', () => {
      let logPath = os.tmpdir() + '/log-test' + Math.floor(Math.random() * 1000)
      expect(() => {
        fs.accessSync(logPath + '/test.log', fs.constants.R_OK | fs.constants.W_OK)
      }).toThrow();
      let log = new LOG({
        path: logPath,
        file: 'test.log'
      });
      expect(fs.accessSync(logPath + '/test.log', fs.constants.R_OK | fs.constants.W_OK)).toBeUndefined()
    })
    it('should log to a file', (done) => {
      let logPath = os.tmpdir() + '/log-test' + Math.floor(Math.random() * 1000)
      let log = new LOG({
        path: logPath,
        file: 'test.log'
      });
      log.fatal('filetest')
      let expected = getDate() + '\t' + hostname + '\t@general/log\tFATAL\tfiletest\n'
      setTimeout(()=>{
        expect(fs.readFileSync(logPath + '/test.log', 'utf8')).toBe(expected)
        done()
      },500 )

    })
  })
  // TODO: log to loki
  // TODO: add mutation test
})