import { v4 as generateUUID } from 'uuid';
import {
  generateUser,
  translate as t,
} from '../../../../helpers/api-integration/v3';
import { apiError } from '../../../../../website/server/libs/apiError';

describe('POST /user/webhook', () => {
  let user; let
    body;

  beforeEach(async () => {
    user = await generateUser();
    body = {
      id: generateUUID(),
      url: 'https://example.com/endpoint',
      type: 'taskActivity',
      enabled: false,
    };
  });

  it('requires a url', async () => {
    delete body.url;

    await expect(user.post('/user/webhook', body)).to.eventually.be.rejected.and.eql({
      code: 400,
      error: 'BadRequest',
      message: 'User validation failed',
    });
  });

  it('requires custom id to be a uuid', async () => {
    body.id = 'not-a-uuid';

    await expect(user.post('/user/webhook', body)).to.eventually.be.rejected.and.eql({
      code: 400,
      error: 'BadRequest',
      message: 'User validation failed',
    });
  });

  it('defaults id to a uuid', async () => {
    delete body.id;

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.id).to.exist;
  });

  it('requires type to be of an accetable type', async () => {
    body.type = 'not a valid type';

    await expect(user.post('/user/webhook', body)).to.eventually.be.rejected.and.eql({
      code: 400,
      error: 'BadRequest',
      message: 'User validation failed',
    });
  });

  it('defaults enabled to true', async () => {
    delete body.enabled;

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.enabled).to.be.true;
  });

  it('can pass a label', async () => {
    body.label = 'Custom Label';

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.label).to.equal('Custom Label');
  });

  it('defaults type to taskActivity', async () => {
    delete body.type;

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.type).to.eql('taskActivity');
  });

  it('ignores protected fields', async () => {
    body.failures = 3;
    body.lastFailureAt = new Date();

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.failures).to.eql(0);
    expect(webhook.lastFailureAt).to.eql(undefined);
  });

  it('successfully adds the webhook', async () => {
    expect(user.webhooks).to.eql([]);

    const response = await user.post('/user/webhook', body);

    expect(response.id).to.eql(body.id);
    expect(response.type).to.eql(body.type);
    expect(response.url).to.eql(body.url);
    expect(response.enabled).to.eql(body.enabled);

    await user.sync();

    expect(user.webhooks).to.not.eql([]);

    const webhook = user.webhooks[0];

    expect(webhook.enabled).to.be.false;
    expect(webhook.type).to.eql('taskActivity');
    expect(webhook.url).to.eql(body.url);
  });

  it('cannot use an id of a webhook that already exists', async () => {
    await user.post('/user/webhook', body);

    await expect(user.post('/user/webhook', body)).to.eventually.be.rejected.and.eql({
      code: 400,
      error: 'BadRequest',
      message: t('webhookIdAlreadyTaken', { id: body.id }),
    });
  });

  it('defaults taskActivity options', async () => {
    body.type = 'taskActivity';

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options).to.eql({
      checklistScored: false,
      created: false,
      updated: false,
      deleted: false,
      scored: true,
    });
  });

  it('can set taskActivity options', async () => {
    body.type = 'taskActivity';
    body.options = {
      checklistScored: true,
      created: true,
      updated: true,
      deleted: true,
      scored: false,
    };

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options).to.eql({
      checklistScored: true,
      created: true,
      updated: true,
      deleted: true,
      scored: false,
    });
  });

  it('discards extra properties in taskActivity options', async () => {
    body.type = 'taskActivity';
    body.options = {
      checklistScored: false,
      created: true,
      updated: true,
      deleted: true,
      scored: false,
      foo: 'bar',
    };

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options.foo).to.not.exist;
    expect(webhook.options).to.eql({
      checklistScored: false,
      created: true,
      updated: true,
      deleted: true,
      scored: false,
    });
  });

  ['created', 'updated', 'deleted', 'scored'].forEach(option => {
    it(`requires taskActivity option ${option} to be a boolean`, async () => {
      body.type = 'taskActivity';
      body.options = {
        [option]: 'not a boolean',
      };

      await expect(user.post('/user/webhook', body)).to.eventually.be.rejected.and.eql({
        code: 400,
        error: 'BadRequest',
        message: t('webhookBooleanOption', { option }),
      });
    });
  });

  it('can set groupChatReceived options', async () => {
    body.type = 'groupChatReceived';
    body.options = {
      groupId: generateUUID(),
    };

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options).to.eql({
      groupId: body.options.groupId,
    });
  });

  it('groupChatReceived options requires a uuid for the groupId', async () => {
    body.type = 'groupChatReceived';
    body.options = {
      groupId: 'not-a-uuid',
    };

    await expect(user.post('/user/webhook', body)).to.eventually.be.rejected.and.eql({
      code: 400,
      error: 'BadRequest',
      message: apiError('groupIdRequired'),
    });
  });

  it('discards extra properties in groupChatReceived options', async () => {
    body.type = 'groupChatReceived';
    body.options = {
      groupId: generateUUID(),
      foo: 'bar',
    };

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options.foo).to.not.exist;
    expect(webhook.options).to.eql({
      groupId: body.options.groupId,
    });
  });

  it('defaults questActivity options', async () => {
    body.type = 'questActivity';

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options).to.eql({
      questStarted: false,
      questFinished: false,
      questInvited: false,
    });
  });

  it('can set questActivity options', async () => {
    body.type = 'questActivity';
    body.options = {
      questStarted: true,
      questFinished: true,
      questInvited: true,
    };

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options).to.eql({
      questStarted: true,
      questFinished: true,
      questInvited: true,
    });
  });

  it('discards extra properties in questActivity options', async () => {
    body.type = 'questActivity';
    body.options = {
      questStarted: false,
      questFinished: true,
      questInvited: true,
      foo: 'bar',
    };

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options.foo).to.not.exist;
    expect(webhook.options).to.eql({
      questStarted: false,
      questFinished: true,
      questInvited: true,
    });
  });

  ['questStarted', 'questFinished', 'questInvited'].forEach(option => {
    it(`requires questActivity option ${option} to be a boolean`, async () => {
      body.type = 'questActivity';
      body.options = {
        [option]: 'not a boolean',
      };

      await expect(user.post('/user/webhook', body)).to.eventually.be.rejected.and.eql({
        code: 400,
        error: 'BadRequest',
        message: t('webhookBooleanOption', { option }),
      });
    });
  });

  it('discards extra properties in globalActivity options', async () => {
    body.type = 'globalActivity';
    body.options = {
      foo: 'bar',
    };

    const webhook = await user.post('/user/webhook', body);

    expect(webhook.options.foo).to.not.exist;
    expect(webhook.options).to.eql({});
  });
});
