/**
 *  RaterFactory
 *
 *  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 {PositiveInteger} from '../../../src/numeric';
import {ServerSideQuote} from '../../server/quote/ServerSideQuote';
import {Session} from '../../server/request/Session';

/**
 * A rater that returns a promise when invoking rating
 */
export interface AsyncRater {
  rate(quote: ServerSideQuote, session: Session): Promise<CommonObject>;
}

/**
 * An error that occurred during rating
 */
export class RateError extends Error {
  constructor(message?: string) {
    super(message);
    Object.setPrototypeOf(this, new.target.prototype);
    this.name = RateError.name;
  }
}

/**
 * Generates async raters on demand
 *
 * Wrap a rater, which is callback-based in this abstraction to use promises.
 */
export class AsyncRaterFactory {
  /**
   * Create a new AsyncRaterFactory
   *
   * @param rater - path to rater
   */
  constructor(private readonly rater: string) {}

  /**
   * Create rater
   *
   * @return rater
   */
  public createRater(): AsyncRater {
    /* eslint-disable @typescript-eslint/no-var-requires */
    const rater = require(this.rater);
    /* eslint-enable @typescript-eslint/no-var-requires */

    const alias = '';

    return {
      async rate(quote: ServerSideQuote, session: Session) {
        const on_failure = (message: string) => {
          throw new RateError(message);
        };

        return new Promise(resolve => {
          rater.rate(quote, session, alias, resolve, on_failure);
        });
      },
    };
  }
}

/** Data required to mock a dummy session */
export interface DummySessionTemplate {
  agentId: PositiveInteger;
  agentName: string;
  agentEntityId: PositiveInteger;
  agencyNumber: string;
}

/**
 * Determine if a session template contains proper fields
 *
 * @param template - hopefully a dummy session template
 *
 * @throws if any fields are not properly set
 */
export const validate_session_template = (template: CommonObject): void => {
  template = template ?? {};

  const missing = [
    'agentId',
    'agentName',
    'agentEntityId',
    'agencyNumber',
  ].filter(field => {
    return template[field] === undefined;
  });

  if (missing.length) {
    throw new Error(
      `Missing session data for the following fields: ${missing.join(', ')}`
    );
  }
};

/**
 * Get a dummy session
 */
export const getDummySession = (template: DummySessionTemplate): Session => {
  return {
    agentId() {
      return template.agentId;
    },

    agentName() {
      return template.agentName;
    },

    agencyNumber() {
      return template.agencyNumber;
    },

    agentEntityId() {
      return template.agentEntityId;
    },

    isInternal() {
      return true;
    },

    userName() {
      return 'Dullahan';
    },

    getData() {
      return {};
    },

    isLoggedIn() {
      return false;
    },
  };
};
