/**
 * Copyright (c) HashiCorp, Inc.
 * SPDX-License-Identifier: BUSL-1.1
 */

import { module, test } from 'qunit';
import {
  visit,
  currentURL,
  click,
  typeIn,
  findAll,
  find,
} from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import { allScenarios } from '../../mirage/scenarios/default';
import { setupMirage } from 'ember-cli-mirage/test-support';
import percySnapshot from '@percy/ember';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';

module('Acceptance | namespaces', function (hooks) {
  setupApplicationTest(hooks);
  setupMirage(hooks);

  test('Namespaces index, general', async function (assert) {
    assert.expect(4);
    allScenarios.namespacesTestCluster(server);
    window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId;
    await visit('/administration/namespaces');
    assert.dom('[data-test-gutter-link="administration"]').exists();
    assert.equal(currentURL(), '/administration/namespaces');
    assert
      .dom('[data-test-namespace-row]')
      .exists({ count: server.db.namespaces.length });
    await a11yAudit(assert);
    await percySnapshot(assert);
    // Reset Token
    window.localStorage.nomadTokenSecret = null;
  });

  test('Prevents namespaes access if you lack a management token', async function (assert) {
    allScenarios.namespacesTestCluster(server);
    window.localStorage.nomadTokenSecret = server.db.tokens[1].secretId;
    await visit('/administration/namespaces');
    assert.equal(currentURL(), '/jobs');
    assert.dom('[data-test-gutter-link="administration"]').doesNotExist();
    // Reset Token
    window.localStorage.nomadTokenSecret = null;
  });

  test('Creating a new namespace', async function (assert) {
    assert.expect(7);
    allScenarios.namespacesTestCluster(server);
    window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId;
    await visit('/administration/namespaces');
    await click('[data-test-create-namespace]');
    assert.equal(currentURL(), '/administration/namespaces/new');
    await typeIn('[data-test-namespace-name-input]', 'My New Namespace');
    await click('button[data-test-save-namespace]');
    assert
      .dom('.flash-message.alert-critical')
      .exists('Doesnt let you save a bad name');
    assert.equal(currentURL(), '/administration/namespaces/new');
    document.querySelector('[data-test-namespace-name-input]').value = ''; // clear
    await typeIn('[data-test-namespace-name-input]', 'My-New-Namespace');
    await click('button[data-test-save-namespace]');
    assert.dom('.flash-message.alert-success').exists();

    assert.equal(
      currentURL(),
      '/administration/namespaces/My-New-Namespace',
      'redirected to the now-created namespace'
    );
    await visit('/administration/namespaces');
    const newNs = [...findAll('[data-test-namespace-name]')].filter((a) =>
      a.textContent.includes('My-New-Namespace')
    )[0];
    assert.ok(newNs, 'Namespace is in the list');
    await click(newNs);
    assert.equal(currentURL(), '/administration/namespaces/My-New-Namespace');
    await percySnapshot(assert);
    // Reset Token
    window.localStorage.nomadTokenSecret = null;
  });

  test('New namespaces have quotas and node_pool properties if Ent', async function (assert) {
    assert.expect(2);
    allScenarios.namespacesTestCluster(server, { enterprise: true });
    window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId;
    await visit('/administration/namespaces');
    await click('[data-test-create-namespace]');

    // Get the dom node text for the description
    const descriptionText = document.querySelector(
      '[data-test-namespace-editor]'
    ).textContent;

    assert.ok(
      descriptionText.includes('Quota'),
      'Includes Quotas in namespace description'
    );
    assert.ok(
      descriptionText.includes(
        'NodePoolConfiguration',
        'Includes NodePoolConfiguration in namespace description'
      )
    );

    // Reset Token
    window.localStorage.nomadTokenSecret = null;
  });

  test('New namespaces hide quotas and node_pool properties if CE', async function (assert) {
    assert.expect(2);
    allScenarios.namespacesTestCluster(server, { enterprise: false });
    window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId;
    await visit('/administration/namespaces');
    await click('[data-test-create-namespace]');

    // Get the dom node text for the description
    const descriptionText = document.querySelector(
      '[data-test-namespace-editor]'
    ).textContent;

    assert.notOk(descriptionText.includes('Quotas'));
    assert.notOk(descriptionText.includes('NodePoolConfiguration'));

    // Reset Token
    window.localStorage.nomadTokenSecret = null;
  });

  test('Modifying an existing namespace', async function (assert) {
    allScenarios.namespacesTestCluster(server);
    window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId;
    await visit('/administration/namespaces');
    await click('[data-test-namespace-row]:first-child a');
    // Table sorts by name by default
    let firstNamespace = server.db.namespaces.sort((a, b) => {
      return a.name.localeCompare(b.name);
    })[0];
    assert.equal(
      currentURL(),
      `/administration/namespaces/${firstNamespace.name}`
    );
    assert.dom('[data-test-namespace-editor]').exists();
    assert.dom('[data-test-title]').includesText(firstNamespace.name);
    await click('button[data-test-save-namespace]');
    assert.dom('.flash-message.alert-success').exists();
    assert.equal(
      currentURL(),
      `/administration/namespaces/${firstNamespace.name}`,
      'remain on page after save'
    );
    // Reset Token
    window.localStorage.nomadTokenSecret = null;
  });

  test('Deleting a namespace', async function (assert) {
    assert.expect(11);
    allScenarios.namespacesTestCluster(server);
    window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId;
    await visit('/administration/namespaces');

    // Default namespace hides delete button
    const defaultNamespaceLink = [
      ...findAll('[data-test-namespace-name]'),
    ].filter((row) => row.textContent.includes('default'))[0];
    await click(defaultNamespaceLink);

    assert.equal(currentURL(), `/administration/namespaces/default`);
    let deleteButton = find('[data-test-delete-namespace] button');
    assert
      .dom(deleteButton)
      .doesNotExist('delete button is not present for default');

    // Standard namespace properly deletes
    await visit('/administration/namespaces');

    let nonDefaultNamespace = server.db.namespaces.findBy(
      (ns) => ns.name != 'default'
    );
    const nonDefaultNsLink = [...findAll('[data-test-namespace-name]')].filter(
      (row) => row.textContent.includes(nonDefaultNamespace.name)
    )[0];
    await click(nonDefaultNsLink);
    assert.equal(
      currentURL(),
      `/administration/namespaces/${nonDefaultNamespace.name}`
    );
    deleteButton = find('[data-test-delete-namespace] button');
    assert.dom(deleteButton).exists('delete button is present for non-default');
    await click(deleteButton);
    assert
      .dom('[data-test-confirmation-message]')
      .exists('confirmation message is present');
    await click(find('[data-test-confirm-button]'));
    assert.dom('.flash-message.alert-success').exists();
    assert.equal(currentURL(), '/administration/namespaces');
    assert
      .dom(`[data-test-namespace-name="${nonDefaultNamespace.name}"]`)
      .doesNotExist();

    // Namespace with variables errors properly
    // "with-variables" hard-coded into scenario to be a NS with variables attached
    await visit('/administration/namespaces/with-variables');
    assert.equal(currentURL(), '/administration/namespaces/with-variables');
    deleteButton = find('[data-test-delete-namespace] button');
    await click(deleteButton);
    await click(find('[data-test-confirm-button]'));
    assert
      .dom('.flash-message.alert-critical')
      .exists('Doesnt let you delete a namespace with variables');

    assert.equal(currentURL(), '/administration/namespaces/with-variables');

    // Reset Token
    window.localStorage.nomadTokenSecret = null;
  });

  test('Deleting a namespace failure and return', async function (assert) {
    // This is an indirect test of rollbackWithoutChangedAttrs
    // which allows deletes to fail and rolls back attributes
    // It was added because this path was throwing an error when
    // reloading the Ember model that was attempted to be deleted

    assert.expect(3);
    allScenarios.namespacesTestCluster(server);
    window.localStorage.nomadTokenSecret = server.db.tokens[0].secretId;

    // Attempt a delete on an un-deletable namespace
    await visit('/administration/namespaces/with-variables');
    let deleteButton = find('[data-test-delete-namespace] button');
    await click(deleteButton);
    await click(find('[data-test-confirm-button]'));

    assert
      .dom('.flash-message.alert-critical')
      .exists('Doesnt let you delete a namespace with variables');
    assert.equal(currentURL(), '/administration/namespaces/with-variables');

    // Navigate back to the page via the index
    await visit('/administration/namespaces');

    // Default namespace hides delete button
    const notDeletedNSLink = [...findAll('[data-test-namespace-name]')].filter(
      (row) => row.textContent.includes('with-variables')
    )[0];
    await click(notDeletedNSLink);

    assert.equal(currentURL(), `/administration/namespaces/with-variables`);
  });
});
