var get = Ember.get, set = Ember.set;
var env, Person, Phone, App;

module("integration/adapter/fixture_adapter - DS.FixtureAdapter", {
  setup: function() {
    Person = DS.Model.extend({
      firstName: DS.attr('string'),
      lastName: DS.attr('string'),

      height: DS.attr('number'),

      phones: DS.hasMany('phone', { async: true })
    });

    Phone = DS.Model.extend({
      person: DS.belongsTo('person')
    });

    env = setupStore({ person: Person, phone: Phone, adapter: DS.FixtureAdapter });
    env.adapter.simulateRemoteResponse = true;

    // Enable setTimeout.
    Ember.testing = false;

    Person.FIXTURES = [];
    Phone.FIXTURES = [];
  },
  teardown: function() {
    Ember.testing = true;

    env.container.destroy();
  }
});

test("should load data for a type asynchronously when it is requested", function() {
  Person.FIXTURES = [{
    id: 'wycats',
    firstName: "Yehuda",
    lastName: "Katz",

    height: 65
  },

  {
    id: 'ebryn',
    firstName: "Erik",
    lastName: "Brynjolffsosysdfon",

    height: 70,
    phones: [1, 2]
  }];

  Phone.FIXTURES = [{
    id: 1,
    person: 'ebryn'
  }, {
    id: 2,
    person: 'ebryn'
  }];

  env.store.find('person', 'ebryn').then(async(function(ebryn) {
    equal(get(ebryn, 'isLoaded'), true, "data loads asynchronously");
    equal(get(ebryn, 'height'), 70, "data from fixtures is loaded correctly");

    return Ember.RSVP.hash({ ebryn: ebryn, wycats: env.store.find('person', 'wycats') });
  }, 1000)).then(async(function(records) {
    equal(get(records.wycats, 'isLoaded'), true, "subsequent requests for records are returned asynchronously");
    equal(get(records.wycats, 'height'), 65, "subsequent requested records contain correct information");

    return get(records.ebryn, 'phones');
  }, 1000)).then(async(function(phones) {
    equal(get(phones, 'length'), 2, "relationships from fixtures is loaded correctly");
  }, 1000));
});

test("should load data asynchronously at the end of the runloop when simulateRemoteResponse is false", function() {
  Person.FIXTURES = [{
    id: 'wycats',
    firstName: "Yehuda"
  }];

  env.adapter.simulateRemoteResponse = false;

  var wycats;

  Ember.run(function() {
    env.store.find('person', 'wycats').then(function(person) {
      wycats = person;
    });
  });

  ok(get(wycats, 'isLoaded'), 'isLoaded is true after runloop finishes');
  equal(get(wycats, 'firstName'), 'Yehuda', 'record properties are defined after runloop finishes');
});

test("should create record asynchronously when it is committed", function() {
  equal(Person.FIXTURES.length, 0, "Fixtures is empty");

  var paul = env.store.createRecord('person', {firstName: 'Paul', lastName: 'Chavard', height: 70});

  paul.on('didCreate', async(function() {
    equal(get(paul, 'isNew'), false, "data loads asynchronously");
    equal(get(paul, 'isDirty'), false, "data loads asynchronously");
    equal(get(paul, 'height'), 70, "data from fixtures is saved correctly");

    equal(Person.FIXTURES.length, 1, "Record added to FIXTURES");

    var fixture = Person.FIXTURES[0];

    ok(typeof fixture.id === 'string', "The fixture has an ID generated for it");
    equal(fixture.firstName, 'Paul');
    equal(fixture.lastName, 'Chavard');
    equal(fixture.height, 70);
  }));

  paul.save();
});

test("should update record asynchronously when it is committed", function() {
  equal(Person.FIXTURES.length, 0, "Fixtures is empty");

  var paul = env.store.push('person', { id: 1, firstName: 'Paul', lastName: 'Chavard', height: 70});

  paul.set('height', 80);

  paul.on('didUpdate', async(function() {
    equal(get(paul, 'isDirty'), false, "data loads asynchronously");
    equal(get(paul, 'height'), 80, "data from fixtures is saved correctly");

    equal(Person.FIXTURES.length, 1, "Record FIXTURES updated");

    var fixture = Person.FIXTURES[0];

    equal(fixture.firstName, 'Paul');
    equal(fixture.lastName, 'Chavard');
    equal(fixture.height, 80);
  }, 1000));

  paul.save();
});

test("should delete record asynchronously when it is committed", function() {
  stop();

  var timer = setTimeout(function() {
    start();
    ok(false, "timeout exceeded waiting for fixture data");
  }, 1000);

  equal(Person.FIXTURES.length, 0, "Fixtures empty");

  var paul = env.store.push('person', { id: 'paul', firstName: 'Paul', lastName: 'Chavard', height: 70 });

  paul.deleteRecord();

  paul.on('didDelete', function() {
    clearTimeout(timer);
    start();

    equal(get(paul, 'isDeleted'), true, "data deleted asynchronously");
    equal(get(paul, 'isDirty'), false, "data deleted asynchronously");

    equal(Person.FIXTURES.length, 0, "Record removed from FIXTURES");
  });

  paul.save();
});

test("should follow isUpdating semantics", function() {
  var timer = setTimeout(function() {
    start();
    ok(false, "timeout exceeded waiting for fixture data");
  }, 1000);

  stop();

  Person.FIXTURES = [{
    id: "twinturbo",
    firstName: "Adam",
    lastName: "Hawkins",
    height: 65
  }];

  var result = env.store.findAll('person');

  result.then(function(all) {
    clearTimeout(timer);
    start();
    equal(get(all, 'isUpdating'), false, "isUpdating is set when it shouldn't be");
  });
});

test("should coerce integer ids into string", function() {
  Person.FIXTURES = [{
    id: 1,
    firstName: "Adam",
    lastName: "Hawkins",
    height: 65
  }];

  env.store.find('person', 1).then(async(function(result) {
    strictEqual(get(result, 'id'), "1", "should load integer model id as string");
  }));
});

test("should coerce belongsTo ids into string", function() {
  Person.FIXTURES = [{
    id: 1,
    firstName: "Adam",
    lastName: "Hawkins",

    phones: [1]
  }];

  Phone.FIXTURES = [{
    id: 1,
    person: 1
  }];

  env.store.find('phone', 1).then(async(function(result) {
    var person = get(result, 'person');
    person.one('didLoad', async(function() {
      strictEqual(get(result, 'person.id'), "1", "should load integer belongsTo id as string");
      strictEqual(get(result, 'person.firstName'), "Adam", "resolved relationship with an integer belongsTo id");
    }));
  }));
});

test("only coerce belongsTo ids to string if id is defined and not null", function() {
  Person.FIXTURES = [];

  Phone.FIXTURES = [{
    id: 1
  }];

  env.store.find('phone', 1).then(async(function(phone) {
    equal(phone.get('person'), null);
  }));
});

test("should throw if ids are not defined in the FIXTURES", function() {
  Person.FIXTURES = [{
    firstName: "Adam",
    lastName: "Hawkins",
    height: 65
  }];

  raises(function(){
    env.store.find('person', 1);
  }, /the id property must be defined as a number or string for fixture/);

  Person.FIXTURES = [{
    id: 0
  }];

  env.store.find('person', 0).then(async(function() {
    ok(true, "0 is an acceptable ID, so no exception was thrown");
  }), function() {
    ok(false, "should not get here");
  });
});
