import {DocCreationInfo} from 'app/common/DocListAPI';
import {DocAPI} from 'app/common/UserAPI';
import {assert, driver, Key} from 'mocha-webdriver';
import * as gu from 'test/nbrowser/gristUtils';
import {server, setupTestSuite} from 'test/nbrowser/testUtils';
import {EnvironmentSnapshot} from 'test/server/testUtils';
import {WebhookFields} from "../../app/common/Triggers";

describe('WebhookOverflow', function () {
  this.timeout(30000);
  const cleanup = setupTestSuite();
  let session: gu.Session;
  let oldEnv: EnvironmentSnapshot;
  let doc: DocCreationInfo;
  let docApi: DocAPI;
  gu.bigScreen();

  before(async function () {
    oldEnv = new EnvironmentSnapshot();
    process.env.ALLOWED_WEBHOOK_DOMAINS = '*';
    process.env.GRIST_MAX_QUEUE_SIZE = '4';
    await server.restart();
    session = await gu.session().teamSite.login();
    const api = session.createHomeApi();
    doc = await session.tempDoc(cleanup, 'Hello.grist');
    docApi = api.getDocAPI(doc.id);
    await api.applyUserActions(doc.id, [
      ['AddTable', 'Table2', [{id: 'A'}, {id: 'B'}, {id: 'C'}, {id: 'D'}, {id: 'E'}]],
      ['AddRecord', 'Table2', null, {}],
    ]);
    const webhookDetails: WebhookFields = {
      url: 'https://localhost/WrongWebhook',
      eventTypes: ["update"],
      enabled: true,
      name: 'test webhook',
      tableId: 'Table2',
      watchedColIds: []
    };
    await docApi.addWebhook(webhookDetails);
    await docApi.addWebhook(webhookDetails);
  });

  after(async function () {
    oldEnv.restore();
    await server.restart();
  });

  async function enterCellWithoutWaitingOnServer(...keys: string[]) {
    const lastKey = keys[keys.length - 1];
    if (![Key.ENTER, Key.TAB, Key.DELETE].includes(lastKey)) {
      keys.push(Key.ENTER);
    }
    await driver.sendKeys(...keys);
  }

  async function getNumWaiting() {
    const cells = await gu.getVisibleDetailCells({col: 'Status', rowNums: [1, 2]});
    return cells.map((cell) => {
      const status = JSON.parse(cell.replace(/\n/g, ''));
      return status.numWaiting;
    });
  }

  async function overflowWebhook() {
    await gu.openPage('Table2');
    await gu.getCell('A', 1).click();
    await gu.enterCell(new Date().toString());
    await gu.getCell('B', 1).click();
    await enterCellWithoutWaitingOnServer(new Date().toString());
    await gu.waitToPass(async () => {
      const toast = await gu.getToasts();
      assert.include(toast, 'New changes are temporarily suspended. Webhooks queue overflowed.' +
        ' Please check webhooks settings, remove invalid webhooks, and clean the queue.\ngo to webhook settings');
    }, 4000);
  }

  async function overflowResolved() {
    await gu.waitForServer();
    await gu.waitToPass(async () => {
      const toast = await gu.getToasts();
      assert.notInclude(toast, 'New changes are temporarily suspended. Webhooks queue overflowed.' +
        ' Please check webhooks settings, remove invalid webhooks, and clean the queue.\ngo to webhook settings');
    }, 12500);
  }

  it('should show a message when overflowed', async function () {
    await overflowWebhook();
  });

  it('message should disappear after clearing queue', async function () {
    await openWebhookPageWithoutWaitForServer();
    assert.deepEqual(await getNumWaiting(), [2, 2]);
    await driver.findContent('button', /Clear Queue/).click();
    await overflowResolved();
    assert.deepEqual(await getNumWaiting(), [0, 0]);
  });

  it('should clear a single webhook queue when that webhook is disabled', async function () {
    await overflowWebhook();
    await openWebhookPageWithoutWaitForServer();
    await gu.waitToPass(async () => {
      assert.deepEqual(await getNumWaiting(), [2, 2]);
    }, 4000);
    await gu.getDetailCell({col: 'Enabled', rowNum: 1}).click();
    await overflowResolved();
    assert.deepEqual(await getNumWaiting(), [0, 2]);
  });
});

async function openWebhookPageWithoutWaitForServer() {
  await openDocumentSettings();
  const button = await driver.findContentWait('a', /Manage Webhooks/i, 3000);
  await gu.scrollIntoView(button).click();
  await waitForWebhookPage();
}

async function waitForWebhookPage() {
  await driver.findContentWait('button', /Clear Queue/, 3000);
  // No section, so no easy utility for setting focus. Click on a random cell.
  await gu.getDetailCell({col: 'Webhook Id', rowNum: 1}).click();
}

export async function openAccountMenu() {
  await driver.findWait('.test-dm-account', 1000).click();
  // Since the AccountWidget loads orgs and the user data asynchronously, the menu
  // can expand itself causing the click to land on a wrong button.
  await driver.findWait('.test-site-switcher-org', 1000);
  await driver.sleep(250);  // There's still some jitter (scroll-bar? other user accounts?)
}

export async function openDocumentSettings() {
  await openAccountMenu();
  await driver.findContent('.grist-floating-menu a', 'Document Settings').click();
  await gu.waitForUrl(/settings/, 5000);
}
