(function () {
  if (!window.cet || !window.cet.microservices || !window.cet.microservices.apiready) {
    window.cet = window.cet || {};
    cet.microservices = cet.microservices || {};

    let apiready = new Promise(function (resolve, reject) {
      var scriptElement = document.createElement("script");
      scriptElement.addEventListener('load', resolve);
      scriptElement.addEventListener('error', reject);
      var origin = document.currentScript.src.split('/', 3).join('/');
      scriptElement.src = origin + "/provider/ApiProvider.js?8dd24ce9cdbec80";
      document.head.appendChild(scriptElement);
    });
    cet.microservices.apiready = apiready;
  }
  let selfUrl = document.currentScript.src;
  cet.microservices.apiready.then(() => {
    cet.microservices.api.system.addService(selfUrl);
  });
})();
window.cet = window.cet || {};
window.cet.microservices = window.cet.microservices || {};

cet.microservices.learningapi = (function () {
  // in order to call 'safe' api safely, make sure accessmanagement is loaded:
  var safeApiReady = cet.microservices.apiready.then(() => {
    if (cet.accessmanagement)
      return Promise.resolve();
    else
      return cet.microservices.api.loadApi("/accessmngapi/provider/accessmanagement.js");
  });

  const settings = {
    apiUrl: "/learningapi/"
  };

  const isDevEnvironment = (location.host.indexOf('.dev') > 0 || location.host.indexOf('.testing') > 0);

  /*------------------------------------------------------------------------------*/
  /* Build URL query from the given payload object. */
  const buildQueryFromPayload = function (payload) {
    if (payload && typeof payload === 'object') {
      var str = [];

      for (var p in payload) {
        if (payload.hasOwnProperty(p)) {
          if (Array.isArray(payload[p])) {
            for (var i = 0; i < payload[p].length; i++) {
              str.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p][i]));
            }
          } else {
            str.push(encodeURIComponent(p) + "=" + encodeURIComponent(payload[p]));
          }
        }
      }

      return str.length > 0 ? '?' + str.join('&') : '';
    }

    return '';
  };

  /*------------------------------------------------------------------------------*/
  /* Validate single parameter. */
  const validateParameter = function (pName, pValue) {
    if ((typeof pValue === 'string' && pValue.trim().length === 0) ||
      (Array.isArray(pValue) && pValue.length === 0)) {
      return pName + ' cannot be empty';
    }

    return '';
  };

  /*------------------------------------------------------------------------------*/
  /* Validate request object. */
  const validateRequest = function (request, relevantParameters) {
    if (!request) {
      return 'request data is missing';
    }

    for (var i = 0; i < relevantParameters.length; i++) {
      var param = relevantParameters[i];
      var paramParts = param.split('.');

      var subRequest = request;

      if (paramParts.length > 1) {
        for (var p = 0; p < paramParts.length - 1; p++) {
          subRequest = subRequest && subRequest[paramParts[p]];
          param = paramParts[p + 1];
        }
      }

      if (!subRequest || !subRequest.hasOwnProperty(param)) {
        return relevantParameters[i] + ' is missing';
      } else {
        var error = validateParameter(relevantParameters[i], subRequest[param]);

        if (error) {
          return error;
        }
      }
    }

    return '';
  };

  /*------------------------------------------------------------------------------*/
  const processRequest = (request) => {
    var promise = new Promise(function (resolve, reject) {
      var validationErrorMsg = request.validationParams ? validateRequest(request.data, request.validationParams) : '';
      if (validationErrorMsg) {
        isDevEnvironment && alert(validationErrorMsg);
        reject({
          'name': 'ProviderValidationError',
          'message': validationErrorMsg
        });
      } else {
        safeApiReady.then(async () => {
          try {
            var result = null;

            if (request.type === 'get') {
              result = await cetms.safe.get(request.URL + buildQueryFromPayload(request.data));
            } else if (request.type === 'post') {
              result = await cetms.safe.post(request.URL, request.data);
            }

            resolve(result);
          } catch (error) {
            reject(error);
          }
        }).catch(error => {
          reject(error);
        });
      }
    });

    return promise;
  };

  //#region APPLICATION

  /*------------------------------------------------------------------------------*/
  const createApplication = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Application/Create',
      data: requestData,
      validationParams: ['id', 'name', 'owners']
    });
  };

  /*------------------------------------------------------------------------------*/
  const updateApplication = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Application/Update',
      data: requestData,
      validationParams: ['applicationId', 'applicationName', 'applicationOwners']
    });
  };

  /*------------------------------------------------------------------------------*/
  const duplicateApplication = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Application/DuplicateApplication',
      data: requestData,
      validationParams: ['sourceApplicationId', 'newApplicationId', 'newApplicationName']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getApplication = (requestData) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'Application/Get',
      data: requestData,
      validationParams: ['applicationId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const deleteApplication = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Application/Delete',
      data: requestData,
      validationParams: ['applicationId']
    });
  };

  //#endregion

  //#region Evaluations

  /*------------------------------------------------------------------------------*/

  const attachApplicationToPath = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Evaluation/AttachApplicationToPath',
      data: requestData,
      validationParams: ['sourceApplicationId', 'targetApplicationId']
    });
  };

  /*------------------------------------------------------------------------------*/

  const calculate = (requestData) => {
    requestData.enforceCalculation = 'Allways';

    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Evaluation/Calculate',
      data: requestData,
      validationParams: ['studentId', 'applicationId', 'nodeIds', 'pathes']
    });
  };

  /*------------------------------------------------------------------------------*/

  const getByPath = (requestData) => {
    requestData.enforceCalculation = requestData.enforceCalculation || 'Never';

    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Evaluation/GetByPath',
      data: requestData,
      validationParams: ['studentId', 'applicationId', 'nodeIds', 'pathes']
    });
  };

  /*------------------------------------------------------------------------------*/

  const recalculateMissingEvaluations = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Evaluation/RecalculateMissingEvaluations',
      data: requestData,
      validationParams: ['applicationIds']
    });
  };

  /*------------------------------------------------------------------------------*/

  //#endregion

  //#region Next
  /*------------------------------------------------------------------------------*/
  const getNextItem = (requestData) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'Next/Get',
      data: requestData,
      validationParams: ['applicationId', 'studentId']
    });
  };

  /*------------------------------------------------------------------------------*/

  //#endregion

  //#region Node
  /*------------------------------------------------------------------------------*/
  const createNode = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Node/Create',
      data: requestData,
      validationParams: ['node.nodeId', 'node.structureId', 'node.structureType']
    });
  };

  /*------------------------------------------------------------------------------*/
  const updateNodePath = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Node/UpdatePath',
      data: requestData,
      validationParams: ['nodeId', 'structureId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const updateNodeData = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Node/UpdateData',
      data: requestData,
      validationParams: ['nodeId', 'structureId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const insertNodes = (requestData) => {
    if (requestData && requestData.nodes) {
      requestData.nodes.forEach(node => {
        node.metadata = node.metadata ? JSON.stringify(node.metadata).replace(/"/g, '\"') : '';
      });
    }

    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Node/InsertNodes',
      data: requestData,
      validationParams: ['nodes', 'structureId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const deleteNode = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Node/Delete',
      data: requestData,
      validationParams: ['nodeId', 'structureId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const deleteAllNodes = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'Node/DeleteAll',
      data: requestData,
      validationParams: ['applicationId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getNodes = (requestData) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'Node/Get',
      data: requestData,
      validationParams: ['applicationId', 'nodeIds']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getLeavesNodes = (applicationId) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'Node/GetLeaves',
      data: typeof applicationId !== 'object' ? { applicationId: applicationId } : applicationId,
      validationParams: ['applicationId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getFullStructure = (applicationId) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'Node/GetFullStructure',
      data: typeof applicationId !== 'object' ? { applicationId: applicationId } : applicationId,
      validationParams: ['applicationId']
    });
  };
  /*------------------------------------------------------------------------------*/
  const getDocumentsNumOfSections = (applicationId) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'Node/GetDocumentsNumOfSections',
      data: typeof applicationId !== 'object' ? { applicationId: applicationId } : applicationId,
      validationParams: ['applicationId']
    });
  };
  /*------------------------------------------------------------------------------*/
  //#endregion

  //#region State
  /*------------------------------------------------------------------------------*/
  const getByApplication = (requestData) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'State/GetByApplication',
      data: requestData,
      validationParams: ['applicationId', 'studentId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getByElementType = (requestData) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'State/GetByElementType',
      data: requestData,
      validationParams: ['applicationId', 'studentId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getByNodeIds = (requestData) => {
    return processRequest({
      type: 'get',
      URL: settings.apiUrl + 'State/GetByNodeIds',
      data: requestData,
      validationParams: ['applicationId', 'studentId']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getMultipleByApplication = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'State/GetMultipleByApplication',
      data: requestData,
      validationParams: ['applicationId', 'studentIds']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getMultipleByElementType = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'State/GetMultipleByElementType',
      data: requestData,
      validationParams: ['applicationId', 'studentIds']
    });
  };

  /*------------------------------------------------------------------------------*/
  const getMultipleByNodeIds = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'State/GetMultipleByNodeIds',
      data: requestData,
      validationParams: ['applicationId', 'studentIds']
    });
  };

  /*------------------------------------------------------------------------------*/
  const update = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'State/Update',
      data: requestData,
      validationParams: ['applicationId', 'studentId', 'nodes']
    });
  };

  /*------------------------------------------------------------------------------*/
  const updateByStatus = (requestData) => {
    return processRequest({
      type: 'post',
      URL: settings.apiUrl + 'State/UpdateByStatus',
      data: requestData,
      validationParams: ['applicationId', 'studentId', 'nodeId']
    });
  };

  /*------------------------------------------------------------------------------*/
  //#endregion

  return {
    'application': {
      create: createApplication,
      update: updateApplication,
      get: getApplication,
      delete: deleteApplication,
      duplicateApplication: duplicateApplication
    },
    'node': {
      create: createNode,
      updatePath: updateNodePath,
      updateData: updateNodeData,
      insertNodes: insertNodes,
      delete: deleteNode,
      deleteAll: deleteAllNodes,
      get: getNodes,
      getLeaves: getLeavesNodes,
      getFullStructure: getFullStructure,
      getDocumentsNumOfSections: getDocumentsNumOfSections
    },
    'state': {
      getByApplication,
      getByElementType,
      getByNodeIds,
      getMultipleByApplication,
      getMultipleByElementType,
      getMultipleByNodeIds,
      update,
      updateByStatus
    },
    'evaluation': {
      attachApplicationToPath,
      calculate,
      getByPath,
      recalculateMissingEvaluations
    },
    'next': {
      get: getNextItem
    }
  };
})();