import { run } from '@ember/runloop';
import { test } from 'ember-qunit';
import wait from 'ember-test-helpers/wait';
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
import moduleForAdapter from '../../helpers/module-for-adapter';

moduleForAdapter('node', 'Unit | Adapter | Node', {
  needs: [
    'adapter:node',
    'model:node-attributes',
    'model:allocation',
    'model:node-driver',
    'model:node-event',
    'model:evaluation',
    'model:job',
    'serializer:application',
    'serializer:node',
    'service:token',
    'service:config',
    'service:watchList',
    'transform:fragment',
    'transform:fragment-array',
  ],
  beforeEach() {
    this.server = startMirage();
    this.server.create('node', { id: 'node-1' });
    this.server.create('node', { id: 'node-2' });
    this.server.create('job', { id: 'job-1', createAllocations: false });

    this.server.create('allocation', { id: 'node-1-1', nodeId: 'node-1' });
    this.server.create('allocation', { id: 'node-1-2', nodeId: 'node-1' });
    this.server.create('allocation', { id: 'node-2-1', nodeId: 'node-2' });
    this.server.create('allocation', { id: 'node-2-2', nodeId: 'node-2' });
  },
  afterEach() {
    this.server.shutdown();
  },
});

test('findHasMany removes old related models from the store', function(assert) {
  let node;
  run(() => {
    // Fetch the model
    this.store.findRecord('node', 'node-1').then(model => {
      node = model;

      // Fetch the related allocations
      return findHasMany(model, 'allocations').then(allocations => {
        assert.equal(
          allocations.get('length'),
          this.server.db.allocations.where({ nodeId: node.get('id') }).length,
          'Allocations returned from the findHasMany matches the db state'
        );
      });
    });
  });

  return wait().then(() => {
    server.db.allocations.remove('node-1-1');

    run(() => {
      // Reload the related allocations now that one was removed server-side
      return findHasMany(node, 'allocations').then(allocations => {
        const dbAllocations = this.server.db.allocations.where({ nodeId: node.get('id') });
        assert.equal(
          allocations.get('length'),
          dbAllocations.length,
          'Allocations returned from the findHasMany matches the db state'
        );
        assert.equal(
          this.store.peekAll('allocation').get('length'),
          dbAllocations.length,
          'Server-side deleted allocation was removed from the store'
        );
      });
    });
  });
});

test('findHasMany does not remove old unrelated models from the store', function(assert) {
  let node;

  run(() => {
    // Fetch the first node and related allocations
    this.store.findRecord('node', 'node-1').then(model => {
      node = model;
      return findHasMany(model, 'allocations');
    });

    // Also fetch the second node and related allocations;
    this.store.findRecord('node', 'node-2').then(model => findHasMany(model, 'allocations'));
  });

  return wait().then(() => {
    assert.deepEqual(
      this.store
        .peekAll('allocation')
        .mapBy('id')
        .sort(),
      ['node-1-1', 'node-1-2', 'node-2-1', 'node-2-2'],
      'All allocations for the first and second node are in the store'
    );

    server.db.allocations.remove('node-1-1');

    run(() => {
      // Reload the related allocations now that one was removed server-side
      return findHasMany(node, 'allocations').then(() => {
        assert.deepEqual(
          this.store
            .peekAll('allocation')
            .mapBy('id')
            .sort(),
          ['node-1-2', 'node-2-1', 'node-2-2'],
          'The deleted allocation is removed from the store and the allocations associated with the other node are untouched'
        );
      });
    });
  });
});

// Using fetchLink on a model's hasMany relationship exercises the adapter's
// findHasMany method as well normalizing the response and pushing it to the store
function findHasMany(model, relationshipName) {
  const relationship = model.relationshipFor(relationshipName);
  return model.hasMany(relationship.key).hasManyRelationship.fetchLink();
}
