import axios from 'axios';
import Validator from 'swagger-model-validator';

/**
 * GenericProtocol defines a simple passthru handler for messages
 * This can be extended to handle different schemas
 *
 * The assumption is that all messages conform to a single
 * wrapper schema definition. Different payloads are handled
 * by a oneOf definitions within the schema determined by a
 * field value.
 *
 * This class can be extended and overridden to handle
 * different schemas and to implement the client specific
 * logic related to be executed when invoked for a given
 * message type.
 *
 * By default encode and decode are just passthru stubs
 *
 * decode is invoked when receiving a message from the backbone
 * encode is invoked before delivering a message to the backbone
 *
 * The intention is that these allow you to transform the message
 * and call invoke internal functions and services as required
 */
class GenericProtocol {
  constructor(schema, services) {
    this.schema = schema;
    this.services = services;
    this.createValidator(schema);
  }

  /**
   * Create a Validator from the provided JSONSchema
   * @param {object} schema
   * @returns {Validator}
   */
  createValidator(schema) {
    this.validator = new Validator(schema);
  }

  /**
   * Validate that a message meets the reqiured schema
   * @param {object} message
   * @returns {object}
   */
  validate(message) {
    return this.validator.validate(
      message,
      this.schema.components.schemas.Message,
      this.schema.components.schemas,
      false,
      false
    );
  }

  /**
   * Identify the payload type from the message content
   * @param {object} message
   * @returns {string}
   */
  getType(message) {
    try {
      return message.payload.message_type;
    } catch (error) {
      return null;
    }
  }

  /**
   * Invoked on receiving a message from the backbone
   *
   * Whilst type isn't used in the generic stub it will
   * be needed by sub-classes overriding the decode method
   * @param {string} type
   * @param {object} message
   * @returns {*}
   */
  decode(type, message) {
    return message;
  }

  /**
   * Invoked on receiving an invalid message from the backbone
   *
   * @param {object} message
   * @param {object} validation
   * @returns {*}
   */
  receivedInvalid(message, validation) {
    return message;
  }

  /**
   * Optionally invoked before delivering a message to the backbone
   *
   * Whilst type isn't used in the generic stub it will
   * be needed by sub-classes overriding the encode method
   * @param {string} type
   * @param {*} message
   * @returns {object}
   */
  encode(type, message) {
    return message;
  }
}

/**
 * GenericSoarProtocol defines a simple passthru handler for messages
 *
 * This provides the same as above but loads a version of the
 * SoAR message protocol
 */
class GenericSoarProtocol extends GenericProtocol {
  constructor(schema, services) {
    super(schema, services);
  }

  /**
   * Create a Validator from the provided JSONSchema
   *
   * If schema is a string build a URL and retrieve the
   * schema from gitlab
   * @param {string|object} schema
   * @returns {object}
   */
  createValidator(schema) {
    if (typeof schema === 'string' && schema.match(/^[\w.-]+$/)) {
      this.loadSchema(schema).then((schema) => {
        this.validator = new Validator(schema);
      });
    } else {
      this.validator = new Validator(schema);
    }
  }

  /**
   * Load schema from gitlab by tag/branch/commitref
   * @param {string} version
   * @returns {object}
   */
  loadSchema(version) {
    this.axios = axios;
    let repository =
      'https://git.noc.ac.uk/communications-backbone-system/backbone-message-format';
    let url = `${repository}/-/raw/${version}/project/soar/swagger.json`;
    return this.axios.get(url).then((response) => {
      this.schema = response.data;
      return response.data;
    });
  }

  /**
   * Validate that a message meets the reqiured schema
   * @param {object} message
   * @returns {object}
   */
  validate(message) {
    return this.validator.validate(
      message,
      this.schema.components.schemas.MESSAGE,
      this.schema.components.schemas,
      false,
      false
    );
  }
}

export { GenericProtocol, GenericSoarProtocol };