var __asyncValues = (this && this.__asyncValues) || function (o) {
  if (!Symbol.asyncIterator)
    throw new TypeError("Symbol.asyncIterator is not defined.");
  var m = o[Symbol.asyncIterator], i;
  return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
  function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
  function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); }
};
import './custom-xhr-override';
import { ApplicationContext, ErrorException, ObjectUtils, ServiceCanceledException, StringUtils, WarningException } from '@sankhyalabs/core';
import { batchRequests } from 'graphql-request';
import UrlUtils from "../../../lib/utils/urlutils";
import { PrintUtils } from '../../utils/PrintUtils';
import { GraphQLRecaller } from './recaller/GraphQLRecaller';
import { ServiceBrokerRecaller } from './recaller/ServiceBrokerRecaller';
import { SnkMessageBuilder } from '../../message/SnkMessageBuilder';
export class DataFetcher {
  constructor() {
    this.ready = true;
    this.watingRequestsById = new Map();
  }
  static getMessage(key, params) {
    if (!this.messageBuilder) {
      this.messageBuilder = new SnkMessageBuilder();
    }
    return this.messageBuilder.getMessage(key, params);
  }
  static get() {
    if (!DataFetcher.instance) {
      DataFetcher.instance = new DataFetcher();
      const application = document.querySelector(this.appTagName);
      if (application === null) {
        DataFetcher.instance.resume();
      }
      else {
        if (ApplicationContext.getContextValue("__SNK__APPLICATION__LOADING__") === true) {
          DataFetcher.instance.pause();
        }
        application.addEventListener('applicationLoading', () => DataFetcher.instance.pause());
        application.addEventListener('applicationLoaded', () => DataFetcher.instance.resume());
      }
    }
    this.instance.application = ApplicationContext.getContextValue("__SNK__APPLICATION__");
    return this.instance;
  }
  static getGraphQLPath() {
    if (StringUtils.isEmpty(this.instance.graphqlPath) && this.instance.application != undefined) {
      this.instance.graphqlPath = `/${this.instance.application.getModuleName()}/graphql`;
    }
    return this.instance.graphqlPath;
  }
  static addRequestListener(listener) {
    const index = DataFetcher.requestListener.indexOf(listener);
    if (index < 0) {
      DataFetcher.requestListener.push(listener);
    }
  }
  static removeRequestListener(listener) {
    const index = DataFetcher.requestListener.indexOf(listener);
    if (index > -1) {
      DataFetcher.requestListener.splice(index, 1);
    }
  }
  processGraphQlErrorInfo(errors) {
    var _a, _b;
    const POSITION_ERROR_INFO = 0;
    const errorTitle = DataFetcher.getMessage("dataFetcher.errorDetected");
    const error = errors[POSITION_ERROR_INFO];
    if (error.length > 0) { //As informações do erro vem no primeiro indice de um array
      const errorInfo = error[POSITION_ERROR_INFO];
      const errorMessage = (errorInfo === null || errorInfo === void 0 ? void 0 : errorInfo.message) ? errorInfo === null || errorInfo === void 0 ? void 0 : errorInfo.message : DataFetcher.getMessage("dataFetcher.noErrorMessage");
      /*
        No HTML5, todos os erros de Troubleshooting são considerados alerta,
        pois a comparação errorLevel == ServiceProxyConstants.LEVEL.ERROR sempre é falsa
        Motivo: - errorLevel é uma string "ERROR" por exemplo
                - ServiceProxyConstants.LEVEL.ERROR é um numero 1, por exemplo
      */
      const errorLevel = ((_a = errorInfo === null || errorInfo === void 0 ? void 0 : errorInfo.extensions) === null || _a === void 0 ? void 0 : _a.level) ? "WARNING" : "ERROR";
      const errorCode = ((_b = errorInfo === null || errorInfo === void 0 ? void 0 : errorInfo.extensions) === null || _b === void 0 ? void 0 : _b.code) ? errorInfo.extensions.code : "";
      switch (errorLevel) {
        case "WARNING":
          return new WarningException(errorTitle, errorMessage, errorCode);
        default:
          return new ErrorException(errorTitle, errorMessage, errorCode);
      }
    }
    else {
      return new ErrorException(errorTitle, DataFetcher.getMessage("dataFetcher.noErrorInfo"));
    }
  }
  processServiceBrokerErrorInfo(serviceBrokerResponse) {
    const title = DataFetcher.getMessage("dataFetcher.errorDetected");
    let { statusMessage, tsError } = serviceBrokerResponse || {};
    if (!statusMessage) {
      statusMessage = DataFetcher.getMessage("dataFetcher.noErrorInfo");
    }
    let tsErrorCode, tsErrorLevel;
    if (tsError) {
      tsErrorCode = tsError.tsErrorCode;
      /*
       No HTML5, todos os erros de Troubleshooting são considerados alerta,
       pois a comparação errorLevel == ServiceProxyConstants.LEVEL.ERROR sempre é falsa
       Motivo: - errorLevel é uma string "ERROR" por exemplo
               - ServiceProxyConstants.LEVEL.ERROR é um numero 1, por exemplo
     */
      tsErrorLevel = "WARNING";
    }
    if (tsErrorLevel === "WARNING") {
      return new WarningException(title, statusMessage, tsErrorCode);
    }
    return new ErrorException(title, statusMessage, tsErrorCode);
  }
  isDevMode() {
    return window === null || window === void 0 ? void 0 : window.hasOwnProperty('wksdata');
  }
  async callGraphQL(req, quietMode = false) {
    var _a;
    if (this.ready) {
      this.pause();
      window.requestAnimationFrame(() => this.resume());
    }
    const reqKey = this.getReqKey(req);
    req.queryID = reqKey;
    req.values.queryID = reqKey;
    if (DataFetcher.clientEventsByID) {
      req.values.registeredEventListeners = [];
      for (let [key] of DataFetcher.clientEventsByID) {
        req.values.registeredEventListeners.push(key);
      }
    }
    if (this.ready) {
      return new Promise(async (resolve, reject) => {
        let query = this.getQueryTemplate(req);
        const res = await this.fecthGraphQL([{ document: query, variables: req.values }], quietMode);
        if (res.errors.length > 0) {
          if (!(await this.proccesGraphQLClientEvents(res === null || res === void 0 ? void 0 : res.extensions, req, resolve, reject))) {
            const error = this.processGraphQlErrorInfo(res.errors);
            reject(error);
          }
        }
        else {
          resolve(res.data[0][reqKey]);
          await this.proccesGraphQLClientEvents(res === null || res === void 0 ? void 0 : res.extensions, req, resolve, reject);
        }
      });
    }
    else {
      if (!this.watingRequestsById.has(reqKey)) {
        this.watingRequestsById.set(reqKey, new WaitingRequest(req, quietMode));
      }
      return (_a = this.watingRequestsById.get(reqKey)) === null || _a === void 0 ? void 0 : _a.promise;
    }
  }
  resolveURL() {
    return UrlUtils.getUrlBase();
  }
  async proccesGraphQLClientEvents(extensions, originalRequest, resolve, reject) {
    if (extensions == undefined) {
      return false;
    }
    for (const idx in extensions) {
      const { clientEvents } = extensions[idx];
      if (!clientEvents) {
        return false;
      }
      for (const id in clientEvents) {
        if (!DataFetcher.hasClientEvent(id)) {
          console.warn(DataFetcher.getMessage("dataFetcher.clientEventNotRegisteredWarn", { id }));
          throw new WarningException(DataFetcher.getMessage("dataFetcher.clientEventNotRegisteredTitle"), DataFetcher.getMessage("dataFetcher.clientEventNotRegisteredMsg", { id }));
        }
        const handlers = DataFetcher.clientEventsByID.get(id);
        handlers.forEach(async (handler) => {
          await handler({ id, content: JSON.parse(clientEvents[id]) }, new GraphQLRecaller(originalRequest, resolve, reject));
        });
      }
      return Object.keys(clientEvents).length > 0;
    }
    return false;
  }
  async processServiceBrokerClientEvents(processParams) {
    var _a, e_1, _b, _c, _d, e_2, _e, _f;
    const { clientEvents, serviceName, payloadJson, accept, reject } = processParams;
    if (!(clientEvents === null || clientEvents === void 0 ? void 0 : clientEvents.length)) {
      return;
    }
    const recaller = new ServiceBrokerRecaller(serviceName, payloadJson, accept, reject);
    try {
      for (var _g = true, clientEvents_1 = __asyncValues(clientEvents), clientEvents_1_1; clientEvents_1_1 = await clientEvents_1.next(), _a = clientEvents_1_1.done, !_a;) {
        _c = clientEvents_1_1.value;
        _g = false;
        try {
          const clientEvent = _c;
          if (DataFetcher.hasClientEvent(clientEvent.id)) {
            const handlers = DataFetcher.clientEventsByID.get(clientEvent.id);
            const handlerClientEvent = {
              id: clientEvent.id,
              content: clientEvent
            };
            try {
              for (var _h = true, handlers_1 = (e_2 = void 0, __asyncValues(handlers)), handlers_1_1; handlers_1_1 = await handlers_1.next(), _d = handlers_1_1.done, !_d;) {
                _f = handlers_1_1.value;
                _h = false;
                try {
                  const handler = _f;
                  await handler(handlerClientEvent, recaller);
                }
                finally {
                  _h = true;
                }
              }
            }
            catch (e_2_1) {
              e_2 = { error: e_2_1 };
            }
            finally {
              try {
                if (!_h && !_d && (_e = handlers_1.return))
                  await _e.call(handlers_1);
              }
              finally {
                if (e_2)
                  throw e_2.error;
              }
            }
          }
        }
        finally {
          _g = true;
        }
      }
    }
    catch (e_1_1) {
      e_1 = { error: e_1_1 };
    }
    finally {
      try {
        if (!_g && !_a && (_b = clientEvents_1.return))
          await _b.call(clientEvents_1);
      }
      finally {
        if (e_1)
          throw e_1.error;
      }
    }
  }
  async processPrinting(payload) {
    if ((payload === null || payload === void 0 ? void 0 : payload.pendingPrinting) === 'true') {
      await PrintUtils.getInstance().processPendingPrinting(payload === null || payload === void 0 ? void 0 : payload.transactionId);
    }
    if ('localPrintings' in payload) {
      PrintUtils.getInstance().processLocalPrinting(payload === null || payload === void 0 ? void 0 : payload.localPrintings);
    }
  }
  getContext(module) {
    var _a;
    const urlParams = UrlUtils.getQueryParams(location.search);
    return {
      baseUrl: `${this.resolveURL()}/${module}/service.sbr`,
      appName: window['APPLICATION_NAME'] || "SankhyaBlocks",
      mgeSession: `${window['mgeSession'] || urlParams.get("mgeSession")}`,
      globalID: window['GLOBALID'] || StringUtils.generateUUID(),
      resourceID: (window["resourceID"] || ((_a = window["workspace"]) === null || _a === void 0 ? void 0 : _a.resourceID))
    };
  }
  async callServiceBroker(serviceName, payload, options) {
    return new Promise(async (accept, reject) => {
      const parts = serviceName.split("@");
      const [module, name] = parts.length === 2 ? parts : ['mge', serviceName];
      const ctx = this.getContext(module);
      let url = `${ctx.baseUrl}?serviceName=${name}&counter=21&application=${ctx.appName}&outputType=json&preventTransform=false&mgeSession=${ctx.mgeSession}&resourceID=${ctx.resourceID}&globalID=${ctx.globalID}&allowConcurrentCalls=true`;
      url = await this.addUrlParams(options, url);
      if (this.isDevMode()) {
        document.cookie = `JSESSIONID=${ctx.mgeSession};`;
      }
      const http = new XMLHttpRequest();
      var payloadJson = {};
      if ('string' == typeof payload) {
        payload = ObjectUtils.stringToObject(payload);
      }
      if (!payload.hasOwnProperty('requestBody')) {
        payloadJson['requestBody'] = payload;
      }
      else {
        payloadJson = payload;
      }
      for (let [key] of DataFetcher.clientEventsByID) {
        if (!payloadJson['requestBody'].hasOwnProperty('clientEventList')) {
          payloadJson['requestBody']['clientEventList'] = {
            clientEvent: []
          };
        }
        payloadJson['requestBody']['clientEventList'].clientEvent.push({ $: key });
      }
      const requestId = StringUtils.generateUUID();
      DataFetcher.requestListener.forEach(listener => listener.onRequestStart({ url: url, requestBody: payload, requestId }));
      http.open("POST", url, true);
      http.withCredentials = true;
      http.send(ObjectUtils.objectToString(payloadJson));
      http.onreadystatechange = async () => {
        var _a;
        if (http.readyState == 4 && http.status == 200) {
          try {
            const jsonResp = JSON.parse(http.responseText);
            /**
             * Este trecho difere do projeto HTML5 devido à decisão de adotar uma abordagem síncrona para esta chamada. Isso é justificado pelo fato de que, no cenário em questão, a abordagem assíncrona teria impactos negativos.
             *
             * Descrição do caso de uso:
             * Ao concluir um recebimento com cartão (Movimentação Financeira), é acionado um EzDialog de confirmação. Posteriormente, a PopUp de impressão é exibida, e, ao ser cancelada, gera a invocação de um novo EzDialog.
             */
            await this.processPrinting(jsonResp);
            if (this.isServiceBrokerError(jsonResp)) {
              reject(this.processServiceBrokerErrorInfo(jsonResp));
            }
            else if (jsonResp.status == DataFetcher.SERVICE_BROKER_STATUS.SERVICE_CANCELED) {
              if (jsonResp.statusMessage) {
                console.debug(`[DataFetcher] ${jsonResp.statusMessage}`);
              }
            }
            else {
              accept(jsonResp.responseBody);
              if (jsonResp.status == DataFetcher.SERVICE_BROKER_STATUS.INFO) {
                (_a = this.application) === null || _a === void 0 ? void 0 : _a.info(jsonResp.statusMessage);
              }
            }
            this.processServiceBrokerClientEvents({ clientEvents: jsonResp === null || jsonResp === void 0 ? void 0 : jsonResp.clientEvents, serviceName, payloadJson, accept, reject });
          }
          catch (err) {
            console.warn(DataFetcher.getMessage("dataFetcher.serviceExecutionErrorWarn", { err }));
            reject(new ErrorException(DataFetcher.getMessage("dataFetcher.serviceExecutionError"), err));
          }
        }
        else if (http.readyState == 4 && http.status != 200) {
          reject(new ErrorException(DataFetcher.getMessage("dataFetcher.serviceExecutionErrorWithName", { serviceName }), http.responseText));
        }
        DataFetcher.requestListener.forEach(listener => listener.onRequestEnd({ url: url, requestBody: payload, requestId }));
      };
    });
  }
  async addUrlParams(options, url) {
    var _a;
    const params = (options === null || options === void 0 ? void 0 : options.urlParams) || {};
    const vss = await ((_a = this.application) === null || _a === void 0 ? void 0 : _a.getAttributeFromHTMLWrapper("VSS"));
    if (!StringUtils.isEmpty(vss)) {
      params.vss = vss;
    }
    const paramKeys = Object.keys(params);
    if (paramKeys.length === 0) {
      return url;
    }
    if (url.indexOf('?') === -1) {
      url += '?';
    }
    for (const key in params) {
      url += `&${key}=${params[key]}`;
    }
    return url;
  }
  isServiceBrokerError(response) {
    return !response.hasOwnProperty('status') ||
      response.status == DataFetcher.SERVICE_BROKER_STATUS.ERROR ||
      response.status == DataFetcher.SERVICE_BROKER_STATUS.TIMEOUT;
  }
  getReqKey(req) {
    return window.btoa(StringUtils.hashCode(`${req.query}${JSON.stringify(req.values || "")}`)).replace(/=/g, "");
  }
  getQueryTemplate(re) {
    return (re.query || "").replaceAll("$queryAlias$", re.queryID);
  }
  pause() {
    this.ready = false;
  }
  async resume() {
    this.ready = true;
    if (this.watingRequestsById.size > 0) {
      const waitinRequests = this.watingRequestsById;
      this.watingRequestsById = new Map();
      this.executePendingRequest(waitinRequests);
    }
  }
  async executePendingRequest(watingRequestsById) {
    var _a, _b;
    const requestsBatch = [];
    let quietMode = true;
    watingRequestsById.forEach((waitingReq) => {
      const query = this.getQueryTemplate(waitingReq.request);
      requestsBatch.push({ document: query, variables: Object.assign({}, waitingReq.request.values) });
      quietMode = quietMode && waitingReq.quietMode;
    });
    const response = await this.fecthGraphQL(requestsBatch, quietMode);
    const { data: dataResponse, errors: errorsResponse, extensions } = response;
    //Reject promises with errors from query
    for (const errorResponse of errorsResponse) {
      const values = Object.values(errorResponse);
      for (const value of values) {
        const waitingRequest = watingRequestsById.get(value.request.variables[value.index].queryID);
        const hasClientEvent = await this.proccesGraphQLClientEvents(extensions, waitingRequest.request, waitingRequest.resolve, waitingRequest.reject);
        const data = await Promise.race([
          waitingRequest.promise,
          new Promise((resolve) => {
            resolve(false);
          })
        ]);
        if (!hasClientEvent || data === false) {
          if (Object.keys(errorResponse[0].extensions).includes("SERVICE_CANCELED") || Object.keys(extensions).includes("isServiceCancelled")) {
            (((_a = watingRequestsById.get(value.request.variables[value.index].queryID)) === null || _a === void 0 ? void 0 : _a.reject) || Promise.reject)(new ServiceCanceledException(DataFetcher.getMessage("dataFetcher.serviceCanceled"), value.message));
          }
          else {
            (((_b = watingRequestsById.get(value.request.variables[value.index].queryID)) === null || _b === void 0 ? void 0 : _b.reject) || Promise.reject)(new ErrorException(DataFetcher.getMessage("dataFetcher.errorDetected"), value.message));
          }
        }
      }
    }
    //Resolve promises with data from query
    for (const data of dataResponse) {
      const entries = Object.entries(data);
      for (const [key, val] of entries) {
        const waitingRequest = watingRequestsById.get(key);
        await this.proccesGraphQLClientEvents(extensions, waitingRequest.request, waitingRequest.resolve, waitingRequest.reject);
        ((waitingRequest === null || waitingRequest === void 0 ? void 0 : waitingRequest.resolve) || Promise.resolve)(val);
      }
    }
  }
  buildGraphQlURL(quietMode) {
    const urlParams = UrlUtils.getQueryParams(location.search);
    const mgeSession = `${window['mgeSession'] || urlParams.get("mgeSession")}`;
    let url = `${this.resolveURL() + DataFetcher.getGraphQLPath()}`;
    if (mgeSession) {
      url += `?mgeSession=${mgeSession}`;
    }
    if (quietMode) {
      url += `${url.indexOf("?") > -1 ? "&" : "?"}quietMode=true`;
    }
    return url;
  }
  async fecthGraphQL(request, quietMode) {
    const dataResponse = [];
    const errorsResponse = [];
    const responseExtensions = [];
    const url = this.buildGraphQlURL(quietMode);
    const requestId = StringUtils.generateUUID();
    DataFetcher.requestListener.forEach(listener => listener.onRequestStart({ url, requestBody: request, requestId }));
    try {
      const response = await batchRequests(url, request, { 'Content-Type': `application/json; charset=UTF-8` });
      response.forEach((resItem, index) => {
        var _a;
        if (((_a = resItem === null || resItem === void 0 ? void 0 : resItem.errors) === null || _a === void 0 ? void 0 : _a.length) > 0) {
          errorsResponse.push(resItem.errors.map((item) => this.normalizeErrorResponse(item, request, index)));
        }
        else {
          dataResponse.push(resItem.data);
        }
        if (resItem.extensions) {
          responseExtensions.push(resItem.extensions);
        }
      });
    }
    catch (err) {
      if (!this.isHttpError(err)) {
        const res = err.response;
        const req = err.request;
        Object.entries(res).forEach(([key, val]) => {
          if (val.errors) {
            errorsResponse.push(val.errors.map((item) => {
              item.request = req;
              item.index = Number(key);
              return item;
            }));
          }
          else if (val.data) {
            dataResponse.push(val.data);
          }
        });
      }
      else {
        DataFetcher.requestListener.forEach(listener => listener.onRequestEnd({ url, requestBody: request, requestId }));
        throw new ErrorException("Falha de comunicação", err.message);
      }
    }
    DataFetcher.requestListener.forEach(listener => listener.onRequestEnd({ url, requestBody: request, requestId }));
    return { data: dataResponse, errors: errorsResponse, extensions: responseExtensions };
  }
  isHttpError(err) {
    var _a;
    return !err.response && !err.request || (((_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.status) >= 300);
  }
  normalizeErrorResponse(error, requests, requestIndex) {
    const req = Object.assign({}, requests[requestIndex]);
    const { variables } = req;
    req.variables = [];
    req.variables[requestIndex] = variables;
    error.request = req;
    error.index = Number(requestIndex);
    return error;
  }
  static addClientEvent(eventID, handler) {
    let clientEvent = DataFetcher.clientEventsByID.get(eventID);
    if (clientEvent != undefined) {
      const hasItem = clientEvent.some((item) => item.toString() === handler.toString());
      if (hasItem)
        return;
      clientEvent.push(handler);
    }
    else {
      DataFetcher.clientEventsByID.set(eventID, [handler]);
    }
  }
  static hasClientEvent(eventID) {
    return DataFetcher.clientEventsByID.has(eventID);
  }
  static removeClientEvent(eventID) {
    return DataFetcher.clientEventsByID.delete(eventID);
  }
}
DataFetcher.appTagName = "snk-application";
DataFetcher.requestListener = [];
DataFetcher.SERVICE_BROKER_STATUS = {
  ERROR: 0,
  INFO: 2,
  OK: 1,
  TIMEOUT: 3,
  SERVICE_CANCELED: 4
};
DataFetcher.clientEventsByID = new Map();
class WaitingRequest {
  constructor(req, quietMode) {
    this._resolve = () => { };
    this._reject = () => { };
    this._request = undefined;
    this._request = req;
    this._promisse = new Promise((resolve, reject) => {
      this._resolve = resolve;
      this._reject = reject;
    });
    this._quietMode = quietMode;
  }
  get resolve() {
    return this._resolve;
  }
  get reject() {
    return this._reject;
  }
  get promise() {
    return this._promisse;
  }
  get request() {
    return this._request;
  }
  get quietMode() {
    return this._quietMode;
  }
}
