/* eslint-disable @typescript-eslint/no-explicit-any */
/*
 * Copyright (C) 2020 StApps
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
import {Logger} from '@openstapps/logger';
import sinon from 'sinon';
import {MailQueue} from '../../src/notification/mail-queue.js';
import {expect} from 'chai';
import Queue from 'promise-queue';
import {MailOptions} from 'nodemailer/lib/sendmail-transport';
import {getTransport, TRANSPORT_SEND_RESPONSE} from '../common.js';

describe('MailQueue', async function () {
  const sandbox = sinon.createSandbox();
  let clock: sinon.SinonFakeTimers;

  beforeEach(() => {
    clock = sandbox.useFakeTimers();
  });

  afterEach(function () {
    clock.restore();
    sandbox.restore();
  });

  it('should fail after maximal number of verification checks', function () {
    const loggerStub = sandbox.spy(Logger, 'warn');
    const test = () => {
      // @ts-expect-error not assignable
      new MailQueue(getTransport(false));
      // fake that VERIFICATION_TIMEOUT was reached more times (one more) than MAX_VERIFICATION_ATTEMPTS
      clock.tick(MailQueue.VERIFICATION_TIMEOUT * (MailQueue.MAX_VERIFICATION_ATTEMPTS + 1));
    };

    expect(() => test()).to.throw();
    expect(loggerStub.callCount).to.be.equal(MailQueue.MAX_VERIFICATION_ATTEMPTS);
  });

  it('should add queued mails to the queue for sending when transport is verified', async function () {
    const queueAddStub = sandbox.stub(Queue.prototype, 'add');
    const numberOfMails = 3;
    const transport = getTransport(false);
    // @ts-expect-error not assignable
    const mailQueue = new MailQueue(transport);
    const mail: MailOptions = {from: 'Foo', subject: 'Foo Subject'};
    for (let i = 0; i < numberOfMails; i++) {
      await mailQueue.push(mail);
    }

    // fake that transport is verified
    transport.isVerified = () => true;
    clock.tick(MailQueue.VERIFICATION_TIMEOUT);

    expect(queueAddStub.callCount).to.be.equal(numberOfMails);
  });

  it('should not add SMTP sending tasks to queue when transport is not verified', function () {
    const queueAddStub = sandbox.stub(Queue.prototype, 'add');
    // @ts-expect-error not assignable
    const mailQueue = new MailQueue(getTransport(false));
    const mail: MailOptions = {};
    mailQueue.push(mail);

    expect(queueAddStub.called).to.be.false;
  });

  it('should add SMTP sending tasks to queue when transport is verified', function () {
    const queueAddStub = sandbox.stub(Queue.prototype, 'add');
    const transport = getTransport(false);
    // @ts-expect-error not assignable
    const mailQueue = new MailQueue(transport);
    const mail: MailOptions = {from: 'Foo', subject: 'Foo Subject'};
    // fake that transport is verified
    transport.isVerified = () => true;
    mailQueue.push(mail);

    expect(queueAddStub.called).to.be.true;
  });

  it('should send SMTP mails when transport is verified', async function () {
    let caught: any;
    sandbox.stub(Queue.prototype, 'add').callsFake(async promiseGenerator => {
      caught = await promiseGenerator();
    });
    const transport = getTransport(false);
    // @ts-expect-error not assignable
    const mailQueue = new MailQueue(transport);
    const mail: MailOptions = {from: 'Foo', subject: 'Foo Subject'};
    // fake that transport is verified
    transport.isVerified = () => true;
    await mailQueue.push(mail);

    expect(caught).to.be.equal(TRANSPORT_SEND_RESPONSE);
  });
});
