/* Copyright (c) 2012-2016 LevelUP contributors
 * See list at <https://github.com/level/levelup#contributing>
 * MIT License <https://github.com/level/levelup/blob/master/LICENSE.md>
 */

var levelup = require('../lib/levelup.js')
  , errors  = levelup.errors
  , fs      = require('fs')
  , common  = require('./common')

  , assert  = require('referee').assert
  , refute  = require('referee').refute
  , buster  = require('bustermove')
  , MemDOWN = require('memdown')

buster.testCase('Init & open()', {
    'setUp': common.commonSetUp
  , 'tearDown': common.commonTearDown

  , 'levelup()': function () {
      assert.isFunction(levelup)
      assert.equals(levelup.length, 3) // location, options & callback arguments
      assert.exception(levelup, 'InitializationError') // no location
    }

  , 'default options': function (done) {
      var location = common.nextLocation()
      levelup(location, { createIfMissing: true, errorIfExists: true }, function (err, db) {
        refute(err, 'no error')
        assert.isTrue(db.isOpen())
        this.closeableDatabases.push(db)
        this.cleanupDirs.push(location)
        db.close(function (err) {
          refute(err)

          assert.isFalse(db.isOpen())

          levelup(location, function (err, db) { // no options object
            refute(err)
            assert.isObject(db)
            assert.isTrue(db.options.createIfMissing)
            assert.isFalse(db.options.errorIfExists)
            assert.equals(db.options.keyEncoding, 'utf8')
            assert.equals(db.options.valueEncoding, 'utf8')
            assert.equals(db.location, location)

            // read-only properties
            db.location = 'foo'
            assert.equals(db.location, location)

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

  , 'basic options': function (done) {
      var location = common.nextLocation()
      levelup(
          location
        , { createIfMissing: true, errorIfExists: true, valueEncoding: 'binary' }
        , function (err, db) {
            refute(err)

            this.closeableDatabases.push(db)
            this.cleanupDirs.push(location)
            assert.isObject(db)
            assert.isTrue(db.options.createIfMissing)
            assert.isTrue(db.options.errorIfExists)
            assert.equals(db.options.keyEncoding, 'utf8')
            assert.equals(db.options.valueEncoding, 'binary')
            assert.equals(db.location, location)


            // read-only properties
            db.location = 'bar'
            assert.equals(db.location, location)

            done()
          }.bind(this)
      )
    }

  , 'options with encoding': function (done) {
      var location = common.nextLocation()
      levelup(
          location
        , { createIfMissing: true, errorIfExists: true, keyEncoding: 'ascii', valueEncoding: 'json' }
        , function (err, db) {
            refute(err)

            this.closeableDatabases.push(db)
            this.cleanupDirs.push(location)
            assert.isObject(db)
            assert.isTrue(db.options.createIfMissing)
            assert.isTrue(db.options.errorIfExists)
            assert.equals(db.options.keyEncoding, 'ascii')
            assert.equals(db.options.valueEncoding, 'json')
            assert.equals(db.location, location)


            // read-only properties
            db.location = 'bar'
            assert.equals(db.location, location)

            done()
          }.bind(this)
      )
    }

  , 'without callback': function (done) {
      var location = common.nextLocation()
        , db = levelup(location, { createIfMissing: true, errorIfExists: true })

      this.closeableDatabases.push(db)
      this.cleanupDirs.push(location)
      assert.isObject(db)
      assert.isTrue(db.options.createIfMissing)
      assert.isTrue(db.options.errorIfExists)
      assert.equals(db.location, location)

      db.on("ready", function () {
        assert.isTrue(db.isOpen())
        done()
      })
    }

  , 'open() with !createIfMissing expects error': function (done) {
      levelup(this.cleanupDirs[0] = common.nextLocation(), { createIfMissing: false }, function (err, db) {
        assert(err)
        refute(db)
        assert.isInstanceOf(err, Error)
        assert.isInstanceOf(err, errors.LevelUPError)
        assert.isInstanceOf(err, errors.OpenError)
        assert(err.notFound === undefined, 'err.notFound is `undefined`, should only be on NotFoundError')
        done()
      }.bind(this))
    }

  , 'open() with createIfMissing expects directory to be created': function (done) {
      levelup(this.cleanupDirs[0] = common.nextLocation(), { createIfMissing: true }, function (err, db) {
        this.closeableDatabases.push(db)
        refute(err)
        assert.isTrue(db.isOpen())
        fs.stat(this.cleanupDirs[0], function (err, stat) {
          refute(err)
          assert(stat.isDirectory())
          done()
        })
      }.bind(this))
    }

  , 'open() with errorIfExists expects error if exists': function (done) {
      levelup(this.cleanupDirs[0] = common.nextLocation(), { createIfMissing: true }, function (err, db) {
        this.closeableDatabases.push(db)
        refute(err) // sanity
        levelup(this.cleanupDirs[0], { errorIfExists   : true }, function (err) {
          assert(err)
          assert.isInstanceOf(err, Error)
          assert.isInstanceOf(err, errors.LevelUPError)
          assert.isInstanceOf(err, errors.OpenError)
          done()
        })
      }.bind(this))
    }

  , 'open() with !errorIfExists does not expect error if exists': function (done) {
      levelup(this.cleanupDirs[0] = common.nextLocation(), { createIfMissing: true }, function (err, db) {
        refute(err) // sanity
        this.closeableDatabases.push(db)
        assert.isTrue(db.isOpen())

        db.close(function () {
          assert.isFalse(db.isOpen())

          levelup(this.cleanupDirs[0], { errorIfExists   : false }, function (err, db) {
            refute(err)
            this.closeableDatabases.push(db)
            assert.isTrue(db.isOpen())
            done()
          }.bind(this))
        }.bind(this))
      }.bind(this))
    }

  , 'constructor with options argument uses factory': function (done) {
      var db = levelup({ db: MemDOWN })
      assert.isNull(db.location, 'location property is null')
      db.on('open', function () {
        assert(db.db instanceof MemDOWN, 'using a memdown backend')
        assert.same(db.db.location, '', 'db location property is ""')
        db.put('foo', 'bar', function (err) {
          refute(err, 'no error')
          db.get('foo', function (err, value) {
            assert.equals(value, 'bar', 'correct value')
            done()
          })
        })
      })
    }

  , 'constructor with only function argument uses factory': function (done) {
      var db = levelup(MemDOWN)
      assert.isNull(db.location, 'location property is null')
      db.on('open', function () {
        assert(db.db instanceof MemDOWN, 'using a memdown backend')
        assert.same(db.db.location, '', 'db location property is ""')
        db.put('foo', 'bar', function (err) {
          refute(err, 'no error')
          db.get('foo', function (err, value) {
            assert.equals(value, 'bar', 'correct value')
            done()
          })
        })
      })
    }
})
