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  
 */
export 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,
      true
    );
  }

  /**
   * 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;
  }

  /**
   * 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  
 */
export 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";
    // TODO check this path resolves for tags
    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,
      true
    );
  }
}