/*
 * 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 {SCBulkRequest, SCThingType} from '@openstapps/core';
import moment from 'moment';
// eslint-disable-next-line unicorn/import-style
import util from 'util';
import {Bulk, BulkStorage} from '../../src/storage/bulk-storage.js';
import {expect} from 'chai';
import {ElasticsearchMock} from '../common.js';
import sinon from 'sinon';
import NodeCache from 'node-cache';
import {backendConfig} from '../../src/config.js';

describe('Bulk Storage', function () {
  describe('Bulk', function () {
    let bulkRequest: SCBulkRequest;
    beforeEach(function () {
      bulkRequest = {source: 'some_source', type: SCThingType.Book};
    });

    it('should create a bulk with the given expiration', function () {
      const expiration = moment().add(3600, 'seconds').toISOString();
      bulkRequest.expiration = expiration;

      const bulk = new Bulk(bulkRequest);

      expect(bulk.expiration).to.be.equal(expiration);
      expect(bulk.state).to.be.equal('in progress');
      expect(bulk.uid).to.not.be.undefined;
    });

    it('should fallback and set expiration when it is not provided', function () {
      const bulk = new Bulk(bulkRequest);

      expect(bulk.expiration).to.not.be.undefined;
    });
  });

  describe('BulkStorage', async function () {
    const sandbox = sinon.createSandbox();
    const bulkRequest = {source: 'some_source', type: SCThingType.Book};
    const bulk = new Bulk(bulkRequest);
    bulk.uid = '123';
    let esMock: sinon.SinonStub;
    let database: ElasticsearchMock;

    beforeEach(function () {
      database = new ElasticsearchMock(backendConfig);
      esMock = sandbox.stub(database, 'bulkExpired');
    });

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

    it('should call appropriate database clean-up method on expire', async function () {
      sandbox.stub(NodeCache.prototype, 'on').withArgs('expired', sinon.match.any).yields(123, bulk);
      new BulkStorage(database);

      expect(esMock.calledWith(bulk)).to.be.true;
    });

    it("should not call appropriate database clean-up method on expire if bulk's state is done", async function () {
      bulk.state = 'done';
      sandbox.stub(NodeCache.prototype, 'on').withArgs('expired', sinon.match.any).yields(123, bulk);
      new BulkStorage(database);

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

    it('should throw an error if the bulk for deletion cannot be read', async function () {
      sandbox.stub(BulkStorage.prototype, 'read').callsFake(() => undefined);
      const bulkStorage = new BulkStorage(database);

      return expect(bulkStorage.delete('123')).to.be.rejected;
    });

    it('should delete a bulk', async function () {
      const readStub = sandbox.stub(BulkStorage.prototype, 'read').callsFake(() => bulk);
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      let caught: any;
      sandbox.stub(NodeCache.prototype, 'del').callsFake(() => (caught = 123));
      // force call
      sandbox
        .stub(util, 'promisify')
        // eslint-disable-next-line @typescript-eslint/no-empty-function,unicorn/consistent-function-scoping
        .callsFake(() => () => {})
        // eslint-disable-next-line unicorn/no-null
        .yields(null);
      const bulkStorage = new BulkStorage(database);

      await bulkStorage.delete(bulk.uid);

      expect(readStub.called).to.be.true;
      expect(caught).to.be.equal(123);
      expect(esMock.called).to.be.true;
    });

    it('should read an existing bulk', async function () {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      let caught: any;
      sandbox.stub(NodeCache.prototype, 'get').callsFake(() => (caught = 123));
      // force call
      sandbox
        .stub(util, 'promisify')
        // eslint-disable-next-line unicorn/consistent-function-scoping,@typescript-eslint/no-empty-function
        .callsFake(() => () => {})
        // eslint-disable-next-line unicorn/no-null
        .yields(null);
      const bulkStorage = new BulkStorage(database);

      await bulkStorage.read(bulk.uid);

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