/**
 *  Tests for Indication Controller
 *
 *  Copyright (C) 2010-2020 R-T Specialty, LLC.
 *
 *  This file is part of the Liza Data Collection Framework.
 *
 *  liza 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
import {expect, use as chai_use} from 'chai';
chai_use(require('chai-as-promised'));

import {AsyncQuoteFactory} from '../../../src/dullahan/quote/QuoteFactory';
import {AsyncRater} from '../../../src/dullahan/program/RaterFactory';
import {Bucket} from '../../../src/bucket/Bucket';
import {EventEmitter} from 'events';
import {MongoServerDao} from '../../../src/server/db/MongoServerDao';
import {PositiveInteger} from '../../../src/numeric';
import {ProgramFactory} from '../../../src/dullahan/program/ProgramFactory';
import {Program} from '../../../src/program/Program';
import {ServerSideQuote} from '../../../src/server/quote/ServerSideQuote';
import {indication as Sut} from '../../../src/dullahan/controllers/IndicationController';
import {mockReq, mockRes} from 'sinon-express-mock';
import {
  HttpClient,
  InvalidUrlError,
} from '../../../src/system/network/HttpClient';

describe('IndicationController', () => {
  describe('create', () => {
    it('fails when an invalid webhook is provided', done => {
      const http_client = createHttpClient();

      http_client.validateUrl = () => {
        throw new InvalidUrlError('');
      };

      const sut = createSutCreate({http_client});
      const req = mockReq(createValidRequestPayload());
      const res = mockRes({});

      sut(req, res).then(_ => {
        expect(res.status.withArgs(422).callCount).to.equal(1);
        expect(
          res.send.withArgs({
            message: 'Request must contain a valid webhook URL.',
          }).callCount
        ).to.equal(1);
        done();
      });
    });

    it('calls webhook when a valid request is processed', done => {
      const tests = [
        'https://some.websitethatdoesnotexist.joe',
        'https%3A%2F%2Fsome.websitethatdoesnotexist.joe',
      ].map(webhook => {
        const sut = createSutCreate();
        const req = mockReq(createValidRequestPayload());
        const res = mockRes({});

        req.query.callback = webhook;

        return sut(req, res).then(() => {
          expect(res.status.withArgs(202).callCount).to.equal(1);
        });
      });

      Promise.all(tests).then(_ => {
        done();
      });
    });

    it('fails when invalid session data are provided', done => {
      const http_client = createHttpClient();
      const given = 'https://some.websitethatdoesnotexist.joe';

      const sut = createSutCreate({http_client});
      const req = mockReq({body: {session: {}}});
      const res = mockRes({});

      req.query.callback = given;

      sut(req, res).then(_ => {
        expect(res.status.withArgs(422).callCount).to.equal(1);
        expect(
          res.send.withArgs({
            message:
              'Missing session data for the following fields: agentId, agentName, agentEntityId, agencyNumber',
          }).callCount
        ).to.equal(1);
        done();
      });
    });
  });
});

const createEventEmitter = () => {
  return <EventEmitter>(<unknown>{
    emit() {},
  });
};

const createHttpClient = () => {
  return <HttpClient>(<unknown>{
    async post() {
      return {};
    },

    validateUrl() {
      return true;
    },
  });
};

const createProgramFactory = () => {
  return <ProgramFactory>(<unknown>{
    createProgram() {
      return {
        bucket: createBucket(),
        program: createProgram(),
      };
    },
  });
};

const createQuoteFactory = () => {
  return <AsyncQuoteFactory>(<unknown>{
    createProgram: () => () => () => () => () => {
      return Promise.resolve({
        quote: createQuote(),
      });
    },
  });
};

const createBucket = () => {
  return <Bucket>{
    getData() {
      return {};
    },
  };
};

const createProgram = () => {
  return <Program>(<unknown>{
    classify(_data: CommonObject) {},
    getId() {
      return 1;
    },
    meta: {
      groups: {},
    },
  });
};

const createQuote = () => {
  return <ServerSideQuote>{
    setData(_data) {},
    getId() {
      return 1;
    },
  };
};

const createRater = () => {
  return <AsyncRater>{
    rate() {
      return Promise.resolve({});
    },
  };
};

const createDao = () => {
  return <MongoServerDao>(<unknown>{
    init(_callback: Function) {},
    getNextQuoteId() {
      return new Promise(resolve => resolve(1));
    },

    saveQuote(_quote: unknown, success: any) {
      return success();
    },
  });
};

const createSutCreate = (dependencies: CommonObject = {}) => {
  const {
    emitter,
    http_client,
    program_factory,
    quote_factory,
    rater,
    dao,
  } = dependencies;

  return Sut.create(<EventEmitter>emitter ?? createEventEmitter())(
    <HttpClient>http_client ?? createHttpClient()
  )(<ProgramFactory>program_factory ?? createProgramFactory())(
    <AsyncQuoteFactory>quote_factory ?? createQuoteFactory()
  )(<AsyncRater>rater ?? createRater())(<MongoServerDao>dao ?? createDao());
};

const createValidRequestPayload = () => {
  return {
    body: {
      bucket: {},
      session: {
        agentId: <PositiveInteger>900000,
        agentName: 'foo',
        agentEntityId: <PositiveInteger>1234567,
        agencyNumber: 'AGT12345',
      },
    },
  };
};
