var assert = require('assert'),
  Model = require('../lib/model'),
  Definition = require('../lib/definition'),
  Types = require('../lib/types'),
  util = require('../lib/util'),
  _ = require('lodash');

describe('Model', function() {
  var User;

  beforeEach(function() {
    User = Model.define('User', function() {
      this.string('firstName');
      this.number('age');
      this.string('gender', { default: 'Male' });
      this.string('socialSecurity');
    });
  });

  describe('#define', function() {
    it('inherits Model', function(done) {
      assert.ok(MyModel.__super__ === Model.prototype);
      assert.ok(new MyModel() instanceof Model);
      done();
    });

    it('has definition', function(done) {
      assert.ok(Model.define('foo', function() {}).definition instanceof Definition);
      done();
    });

    it('sets modelName', function(done) {
      assert.equal(User.modelName, 'User');
      done();
    });
  });

  describe('constructor', function() {
    it('sets properties & defaults', function(done) {
      var user = new User({ firstName: 'John', 'age': 30 });
      assert.equal(user.firstName, 'John');
      assert.equal(user.age, 30);
      assert.equal(user.gender, 'Male');
      assert.equal(user.socialSecurity, undefined);
      done();
    });
  });

  describe('#hydrate', function() {
    var MyModel;

    beforeEach(function() {
      MyModel = Model.define('foo', function() {
        this.string('someString');
        this.number('someNumber');
      });
    });

    it('hydrates Model', function(done) {
      var data = {
          someString: 'abcd123',
          someNumber: 1337
        },
        expected = new MyModel(data);

      assert.deepEqual(MyModel.hydrate(data), expected);
      done();
    });

    it('throws error when invalid types are given', function(done) {
      var error;
      try {
        MyModel.hydrate({ someString: 1 });
      } catch (e) {
        error = e;
      }

      assert.ok(error instanceof Error);
      assert.equal(error.message, 'Error transforming field "someString" with type "number" from the database.');
      done();
    });
  });

  describe('validation', function() {
    it('validates', function(done) {
      var validatorCalled = false;

      var alwaysInvalidType = new Types.Type('alwaysInvalid');
      alwaysInvalidType.use(function(value) {
        validatorCalled = true;
      });

      var MyModel = Model.define('MyModel', function() {
        this.type(alwaysInvalidType);
      });

      var model = new MyModel();

      MyModel.validate(model, function(err, validatedModel) {
        assert.equal(err, null);
        assert.equal(validatedModel, model);
        assert.ok(validatorCalled === true);

        done();
      });
    });

    it('throws error on invalid callback', function(done) {
      var error, user = new User();

      try {
        User.validate(user);
      } catch (e) {
        error = e;
      }

      assert.ok(error instanceof Error);
      assert.equal(error.message, 'Expected valid callback for Model.validate().');
      done();
    });

    it('calls validations and returns errors when invalidation occurs', function(done) {
      var validatorCalled = false;

      var alwaysInvalidType = new Types.Type('alwaysInvalid');
      alwaysInvalidType.use(function(value) {
        validatorCalled = true;
        this.invalidate('invalid!');
      });

      var MyModel = Model.define('MyModel', function() {
        this.type(alwaysInvalidType);
      });

      var model = new MyModel();

      MyModel.validate(model, function(err, validatedModel) {
        assert.equal(validatedModel, undefined);
        assert.ok(validatorCalled === true);
        assert.deepEqual(err, [{ name: 'ValidationError', property: 'alwaysInvalid', errors: ['invalid!']}]);

        done();
      });
    });
  });
});
      var MyModel = Model.define('foo', function() {});