import { parseGridConfig, parseLegacyTree } from '../../../../components/snk-pesquisa/pesquisaHelper';
import { ApplicationContext, DataType, ErrorException } from '@sankhyalabs/core';
import { gql } from 'graphql-request';
import { DataFetcher } from '../DataFetcher';
export class PesquisaFetcher {
  constructor() {
    this._defaultPageSize = 100;
    this._templateByQuery = new Map();
    this._searchListenersByDataUnit = new Map();
    this.buldTemplates();
  }
  buldTemplates() {
    this._templateByQuery.set("search", gql `query($entityName: String! $argument: String $criteria: InputSearchCriteria $options: InputSearchOptions) {
                $queryAlias$: search(entityName: $entityName argument: $argument criteria: $criteria options: $options){
                    value
                    label
                }
            }`);
  }
  loadSearchOptions(entityName, argument, criteria, options) {
    var _a;
    const cleanText = (argument === null || argument === void 0 ? void 0 : argument.toString().trim()) || undefined;
    argument = isNaN(Number(cleanText)) && cleanText ? `%${cleanText}` : cleanText;
    criteria === null || criteria === void 0 ? void 0 : criteria.params.forEach(param => {
      if (param.dataType === DataType.OBJECT) {
        param.value = JSON.stringify(param.value);
      }
    });
    const listenerResult = this.applySearchListener(SearchListenerType.beforeSearch, entityName, argument, criteria, options);
    const values = {
      argument: (listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.argument) || argument,
      entityName,
      criteria: (listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.criteria) || criteria,
      options: (listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.searchOptions) || options,
    };
    if (values.options) {
      (_a = values.options) === null || _a === void 0 ? true : delete _a.dataUnitId;
    }
    return new Promise((resolve, reject) => {
      DataFetcher.get()
        .callGraphQL({
        values,
        query: this._templateByQuery.get("search"),
      })
        .then((result) => {
        resolve(result);
      })
        .catch((error) => {
        reject(error);
      });
    });
  }
  buildDescriptionCacheKey(entityName, codeValue) {
    return `${entityName}__${codeValue}`;
  }
  async loadByCode(entityName, codeValue, criteria, searchOptions) {
    const cacheKey = this.buildDescriptionCacheKey(entityName, codeValue);
    const cachedValue = PesquisaFetcher.DESCRIPTIONS_BY_CODE[cacheKey];
    if (cachedValue) {
      return Promise.resolve(cachedValue);
    }
    const result = await this.loadAdvancedSearch(entityName, undefined, criteria, searchOptions, codeValue);
    PesquisaFetcher.DESCRIPTIONS_BY_CODE[cacheKey] = result;
    return result;
  }
  loadAdvancedSearch(entityName, argument, criteria, searchOptions, codeValue) {
    var _a, _b, _c, _d, _e;
    const listenerResult = this.applySearchListener(SearchListenerType.beforeSearch, entityName, argument, criteria, searchOptions);
    const values = {
      argument: (listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.argument) || argument,
      criteria: (listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.criteria) || criteria,
      searchOptions: Object.assign(Object.assign({}, searchOptions), listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.searchOptions),
    };
    const serviceName = "PesquisaSP.getSuggestion";
    const externalCriteria = {
      query: {
        $: (_a = values.criteria) === null || _a === void 0 ? void 0 : _a.expression
      }
    };
    if (((_c = (_b = values.criteria) === null || _b === void 0 ? void 0 : _b.params) === null || _c === void 0 ? void 0 : _c.length) > 0) {
      externalCriteria.params = {
        param: values.criteria.params.map(param => {
          let value = param.value;
          if (typeof value === "string") {
            const match = /CTX\{([^}]+)\}/.exec(value);
            if (match) {
              value = ApplicationContext.getContextValue(`__SNK__APPLICATION__FILTER__CONTEXT(${match[1]})__`);
            }
          }
          if (param.dataType === DataType.NUMBER && value === undefined) {
            throw new ErrorException("Falha detectada", `O campo ${param.name} deve ser informado.`);
          }
          let type = param.dataType;
          if (type === DataType.OBJECT) {
            value = value.value;
            type = "S";
          }
          else {
            type = convertParamType(param.dataType);
          }
          return { $: value, type };
        })
      };
    }
    const options = searchOptions != undefined
      ? Object.assign(Object.assign({}, values === null || values === void 0 ? void 0 : values.searchOptions), { "pkFieldName": searchOptions.codeFieldName, "label": searchOptions.descriptionFieldName, "fieldName": searchOptions.codeFieldName, "useDescriptionOptions": false, "enableRowsCounter": true }) : undefined;
    const reqBody = {
      "serviceName": serviceName,
      "requestBody": {
        "criteria": Object.assign({ "entityName": entityName, "compacted": false, "ignoreEntityCriteria": (_d = options === null || options === void 0 ? void 0 : options.ignoreEntityCriteria) !== null && _d !== void 0 ? _d : false, "limit": this._defaultPageSize, "orderByDesc": false, "externalCriteria": externalCriteria, "localEntityName": (_e = values.searchOptions) === null || _e === void 0 ? void 0 : _e.rootEntity }, { options }),
        "clientEventList": {
          "clientEvent": []
        }
      }
    };
    if (codeValue == undefined) {
      reqBody.requestBody.criteria.query = {
        "$": argument
      };
    }
    else {
      reqBody.requestBody.criteria.pk = {
        "$": codeValue
      };
    }
    const urlOptions = {
      urlParams: {
        "quietMode": true
      }
    };
    return new Promise((resolve, reject) => {
      DataFetcher.get()
        .callServiceBroker("PesquisaSP.getSuggestion", JSON.stringify(reqBody), urlOptions)
        .then(result => resolve(result))
        .catch(error => reject(error));
    });
  }
  addSearchListener(entityName, dataUnitID, listener) {
    var _a;
    const dataUnitSearchListeners = this._searchListenersByDataUnit.get(dataUnitID) || [];
    const entityListener = dataUnitSearchListeners.find(currentListener => currentListener.entity === entityName);
    if (!entityListener) {
      this._searchListenersByDataUnit.set(dataUnitID, [...dataUnitSearchListeners, { entity: entityName, listener }]);
    }
    else {
      for (const type of Object.keys(listener)) {
        if (type in entityListener.listener) {
          const listenerFunctionIsEquals = ((_a = entityListener.listener[type]) === null || _a === void 0 ? void 0 : _a.toString()) === listener[type].toString();
          if (listenerFunctionIsEquals)
            continue;
          entityListener.listener[type] = listener[type];
        }
      }
    }
    return () => {
      const newListeners = dataUnitSearchListeners.filter(currentListener => currentListener.entity !== entityName);
      if (!newListeners.length) {
        this._searchListenersByDataUnit.delete(dataUnitID);
        return;
      }
      this._searchListenersByDataUnit.set(dataUnitID, newListeners);
    };
  }
  applySearchListener(listenerType, entityName, argument, criteria, searchOptions) {
    var _a;
    const dataUnitId = searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.dataUnitId;
    if (!dataUnitId)
      return;
    const entityListener = (_a = this._searchListenersByDataUnit.get(dataUnitId)) === null || _a === void 0 ? void 0 : _a.find(({ entity }) => entity === entityName);
    if (!entityListener)
      return;
    const { listener } = entityListener;
    if (!(listenerType in listener))
      return;
    return listener[listenerType]({ argument, criteria, searchOptions });
  }
  async loadPresentationConfig(entityName) {
    var _a, _b;
    let payload = buildLoadConfig(entityName);
    const response = await DataFetcher.get().callServiceBroker("SystemUtilsSP.getConf", JSON.stringify(payload));
    return (_b = (_a = response === null || response === void 0 ? void 0 : response.config) === null || _a === void 0 ? void 0 : _a.lastUsedMode) !== null && _b !== void 0 ? _b : 'list';
  }
  async savePresentationConfig(entityName, lastUsedMode) {
    let payload = buildSaveConfig(entityName, lastUsedMode);
    await DataFetcher.get().callServiceBroker("SystemUtilsSP.saveConf", JSON.stringify(payload));
  }
  async loadPesquisaGridConfig(entityName) {
    const payload = buildLoadGridConfigPayload(entityName);
    const response = await DataFetcher.get().callServiceBroker("GridConfig.getGridConfigFromJson", JSON.stringify(payload));
    return parseGridConfig(response);
  }
  async savePesquisaGridConfig(entityName, columns) {
    const payload = buildSaveGridConfigPayload(entityName, columns);
    await DataFetcher.get().callServiceBroker("GridConfig.saveGridConfigFromJSON", JSON.stringify(payload));
  }
  async loadTree(entityName, argument, criteria, searchOptions) {
    const requestBody = this.buildLoadTreeRequestBody(entityName, argument, criteria, searchOptions);
    const response = await DataFetcher.get().callServiceBroker("Pesquisa.getStructuredData", JSON.stringify(requestBody));
    return parseLegacyTree(response);
  }
  buildLoadTreeRequestBody(entityName, argument, criteria, searchOptions) {
    var _a, _b;
    const listenerResult = this.applySearchListener(SearchListenerType.beforeSearch, entityName, argument, criteria, searchOptions);
    const incomeSearchOptions = (_a = listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.searchOptions) !== null && _a !== void 0 ? _a : searchOptions;
    const rootEntity = incomeSearchOptions === null || incomeSearchOptions === void 0 ? void 0 : incomeSearchOptions.rootEntity;
    const codeFieldName = incomeSearchOptions === null || incomeSearchOptions === void 0 ? void 0 : incomeSearchOptions.codeFieldName;
    const expression = (_b = listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.criteria) === null || _b === void 0 ? void 0 : _b.expression;
    const params = this.buildParams(listenerResult);
    const instancia = {
      'nome': entityName,
      'nomeInstanciaLocal': rootEntity,
      'codeFieldName': codeFieldName,
    };
    if (expression) {
      instancia['criterioLiteral'] = {
        'expressao': {
          '$': expression,
        },
        'parametro': params,
      };
    }
    return {
      'instancia': instancia,
    };
  }
  buildParams(listenerResult) {
    var _a, _b;
    let params = (_b = (_a = listenerResult === null || listenerResult === void 0 ? void 0 : listenerResult.criteria) === null || _a === void 0 ? void 0 : _a.params) !== null && _b !== void 0 ? _b : [];
    params = params.map(param => {
      return Object.assign(Object.assign({}, param), { tipo: convertParamType(param.dataType), $: param.value });
    });
    return params;
  }
}
PesquisaFetcher.DESCRIPTIONS_BY_CODE = {};
function buildLoadGridConfigPayload(entityName) {
  return {
    'gridConfig': {
      'configName': `GrdCfgHtml5:grdcfg.dynaform.pesquisa.${entityName}`,
    },
  };
}
function buildSaveGridConfigPayload(entityName, columns) {
  const field = {};
  columns === null || columns === void 0 ? void 0 : columns.forEach((col, index) => field[`field${index}`] = col);
  return {
    'gridConfig': {
      'compactMode': true,
      'configName': `GrdCfgHtml5:grdcfg.dynaform.pesquisa.${entityName}`,
      field,
    },
  };
}
function buildLoadConfig(entityName) {
  return {
    'config': {
      'chave': `PesquisaContent:${entityName}`,
      'tipo': 'T',
    },
  };
}
function buildSaveConfig(entityName, lastUsedMode) {
  return {
    'config': {
      lastUsedMode,
      'chave': `PesquisaContent:${entityName}`,
      'tipo': 'T',
    }
  };
}
function convertParamType(dataType) {
  // Alerta: Cuidado para não contaminar o DataType com a implementação atual da pesquisa!
  // Em geral, somente inteiros, data (com ou sem hora) e string são realmente relevantes.
  switch (dataType) {
    case DataType.NUMBER:
      return "I";
    case DataType.DATE:
      return "D";
    default:
      return "S";
  }
}
export var SearchListenerType;
(function (SearchListenerType) {
  SearchListenerType["beforeSearch"] = "beforeSearch";
})(SearchListenerType || (SearchListenerType = {}));
