/**
Copyright: 2024 Contrast Security, Inc
  Contact: support@contrastsecurity.com
  License: Commercial

  NOTICE: This Software and the patented inventions embodied within may only be
  used as part of Contrast Security’s commercial offerings. Even though it is
  made available through public repositories, use of this Software is subject to
  the applicable End User Licensing Agreement found at
  https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
  between Contrast Security and the End User. The Software may not be reverse
  engineered, modified, repackaged, sold, redistributed or otherwise used in a
  way not consistent with the End User License Agreement.
*/
'use strict';
const {
  settings,
  service: { createGRPCClient }
} = require('@contrast/protobuf-api');

const { MESSAGE_TYPE } = require('../constants');

/**
 * Client to use when gRPC is enabled for SpeedRacer communication. This will
 * accept (in .send()) the same statically generated models as used in legacy
 * RPC, but will be sent to new gRPC service endpoint. The gRPC library will
 * handle serialization from and deserialization into the models.
 */
class GRPCClient {
  constructor({ port, host }) {
    this.address = `${host}:${port}`;
    this.serviceClient = createGRPCClient(this.address);
    this.name = 'grpc-client';
  }

  /**
   * Empty protobuf model. Message deserialization will call .toObject().
   */
  get defaultResponse() {
    return new settings.AgentSettings();
  }

  /**
   * Whether the error represents a connection failure signifying that the
   * service is not running. (additional checks might be required because
   * when TS is killed there are problems.)
   * See: https://grpc.github.io/grpc/core/md_doc_statuscodes.html
   *
   * @param {Error} err
   * @returns {Boolean}
   */
  isConnectFailureError(err) {
    return err.code === 14;
  }

  /**
   * This is just a thin wrapper around single gRPC service method
   * .sendAgentMessage().
   * @param {Message} model the protobuf Message model
   * @returns {Promise}
   */
  async send(requestModel) {
    if (!requestModel) {
      return Promise.reject(
        new Error('grpc-client.send() called with empty requestModel')
      );
    }

    // it appears that if you try to sendAgentMessage and gRPC is not enabled
    // then the 3rd call just hangs and I have no idea why
    return new Promise((resolve, reject) => {
      this.serviceClient.sendAgentMessage(requestModel, function(
        error,
        responseModel
      ) {
        if (error) {
          // make clear which message type failed for debugging
          error.messageType = requestModel[MESSAGE_TYPE];
          reject(error);
        } else {
          resolve(responseModel);
        }
      });
    });
  }
}

module.exports = GRPCClient;
