import { ApplicationContext, StringUtils, UserInterface, } from '@sankhyalabs/core';
import { DataFetcher } from '../../../DataFetcher';
import DataUnitFetcher from '../dataunit-fetcher';
import InMemoryLoader from '../../../../../dataUnit/InMemoryLoader';
import { getRecordValue } from '../../../../../dataUnit/ValueFormatter';
export class DatasetStrategy {
  canSlice() {
    return false;
  }
  processSortingSide(request, dataUnit, serverSideFilters) {
    var _a;
    const localSorting = [];
    const serverSorting = [];
    if (request.sort != undefined) {
      if (serverSideFilters.length === 0) {
        return { localSorting: request.sort, serverSorting: [] };
      }
      for (const sort of request.sort) {
        const descriptor = dataUnit.getField(sort.field);
        const local = ((_a = descriptor === null || descriptor === void 0 ? void 0 : descriptor.properties) === null || _a === void 0 ? void 0 : _a.calculated) === "true" || (descriptor === null || descriptor === void 0 ? void 0 : descriptor.userInterface) === UserInterface.LONGTEXT;
        if (local) {
          localSorting.push(sort);
        }
        else {
          serverSorting.push(sort);
        }
      }
    }
    return { localSorting, serverSorting };
  }
  async load(dataUnit, request, loadingInfo) {
    var _a, _b;
    if (dataUnit.metadata == undefined) {
      return Promise.resolve({ records: [], loadingInfo });
    }
    try {
      const serverSideFilters = (_b = (_a = request.filters) === null || _a === void 0 ? void 0 : _a.filter(filter => !filter.name.startsWith("FILTRO_COLUNA_"))) !== null && _b !== void 0 ? _b : [];
      const { localSorting, serverSorting } = this.processSortingSide(request, dataUnit, serverSideFilters);
      const fields = this.getFieldsList(dataUnit);
      const serviceName = "DatasetSP.loadRecords";
      const requestBody = this.buildRequestBody(serviceName, fields, dataUnit, request, loadingInfo, serverSorting, serverSideFilters);
      const params = loadingInfo.quiet ? { urlParams: { quietMode: "true" } } : undefined;
      const { result: responseRecords, pagerID: pagerId, askRowsLimit } = await DataFetcher.get().callServiceBroker(serviceName, requestBody, params);
      const records = this.processRecords(dataUnit, fields, responseRecords);
      const loadingInProgress = pagerId != undefined;
      const count = loadingInfo.count + records.length;
      const isInsertingNewRecord = dataUnit.hasNewRecord();
      const hasLocalSorting = localSorting.length > 0;
      const needReload = !loadingInProgress && hasLocalSorting;
      /**
       * Caso precise recarregar dados, porém está no meio de uma inserção, os dados não devem recarregados instantaneamente,
       * Mas sim, sinalizar que o dataUnit está aguardando para der recarregado.
       */
      if (needReload && isInsertingNewRecord) {
        dataUnit.setWaitingToReload(true);
      }
      return Promise.resolve({
        records,
        loadingInfo: Object.assign(Object.assign({}, loadingInfo), { pagerId,
          loadingInProgress, total: loadingInProgress ? undefined : count, count, needReload: needReload && !isInsertingNewRecord, askRowsLimit,
          hasLocalSorting })
      });
    }
    catch (error) {
      console.error(error);
      return Promise.reject(error);
    }
  }
  async loadRowMetadata(snkDataUnit, fieldName, metadataName, updatedFields = {}) {
    try {
      const serviceName = "DatasetSP.loadRowMetadata";
      const parsedRequestBody = await this.buildRequestBodyLoadRowMetadata({ snkDataUnit, fieldName, metadataName, serviceName, updatedFields });
      const response = await DataFetcher.get().callServiceBroker(serviceName, parsedRequestBody);
      return response;
    }
    catch (error) {
      console.error(error);
      return Promise.reject(error);
    }
  }
  async buildRequestBodyLoadRowMetadata({ snkDataUnit, serviceName, fieldName, metadataName, updatedFields, }) {
    const dataUnit = snkDataUnit.dataUnit;
    const entityName = DataUnitFetcher.parseDataUnitName(dataUnit.name).entityName;
    const recordIdInfo = await snkDataUnit.getSelectedRecordsIDsInfo();
    const pk = recordIdInfo.reduce((accumulator, currentValue) => {
      accumulator[currentValue.name] = currentValue.value;
      return accumulator;
    }, {});
    const record = dataUnit.getSelectedRecord();
    const fields = dataUnit.metadata.fields.filter(({ standAlone, name }) => !standAlone && !name.includes(".")).map(({ name }) => name);
    const values = fields.reduce((accumulator, currentValue, currentIndex) => {
      const recordValue = getRecordValue(record, currentValue);
      const updatedFieldValue = getRecordValue(updatedFields, currentValue);
      accumulator[currentIndex] = updatedFieldValue !== undefined ? updatedFieldValue : recordValue;
      return accumulator;
    }, {});
    const requestBody = {
      serviceName,
      requestBody: {
        dataSetID: dataUnit.dataUnitId,
        entityName,
        standAlone: false,
        metadataName,
        fieldName,
        fields,
        record: {
          pk,
          oldPk: pk,
          values,
        },
      }
    };
    return JSON.stringify(requestBody);
  }
  getFieldsList(dataUnit) {
    let fields = ["__record__id__", "__record__label__"];
    dataUnit.metadata.fields.forEach((descriptor) => {
      if (descriptor.standAlone)
        return;
      fields = fields.concat(this.getFieldNames(descriptor));
    });
    return fields;
  }
  getStandAloneFieldsList(dataUnit) {
    let fields = {};
    dataUnit.metadata.fields.forEach((currentField) => {
      if (!currentField.standAlone)
        return;
      fields = Object.assign(Object.assign({}, fields), { [currentField.name]: { fieldType: currentField.dataType, userType: currentField.userInterface } });
    });
    return fields;
  }
  getFieldNames(descriptor) {
    const descriptionField = this.getSearchDescriptionField(descriptor);
    if (descriptionField == undefined) {
      return descriptor.name;
    }
    return [descriptor.name, descriptionField];
  }
  buildRequestBody(serviceName, fields, dataUnit, request, loadingInfo, sorting, serverSideFilters) {
    const dataSetID = dataUnit.dataUnitId;
    const dataUnitName = dataUnit.name;
    const entityName = DataUnitFetcher.parseDataUnitName(dataUnitName).entityName;
    const useParallelLoader = !!(request.limit || request.offset);
    const requestBody = {
      serviceName,
      requestBody: {
        dataSetID,
        fields,
        entityName,
        pageNumber: loadingInfo.pageNumber,
        totalRecordsCount: loadingInfo.count,
        pagerID: loadingInfo.pagerId,
        standAlone: false,
        standAloneFieldsMD: Object.assign({ __record__id__: { "fieldType": "S", "userType": "P" }, __record__label__: { "fieldType": "S", "userType": "P" } }, this.getStandAloneFieldsList(dataUnit)),
        tryJoinedFields: true,
        parallelLoader: useParallelLoader,
        crudListener: `br.com.sankhya.bff.${this.getModuleName()}.BFFDataUnitDatasetAdapter`,
        txProperties: this.getTxProperties(dataUnit, request, sorting, serverSideFilters),
        useDefaultRowsLimit: false
      }
    };
    return JSON.stringify(requestBody);
  }
  getModuleName() {
    const app = ApplicationContext.getContextValue("__SNK__APPLICATION__");
    const moduleName = app.getModuleName();
    return moduleName.replace("-bff", "");
  }
  getTxProperties(dataUnit, request, sorting, serverSideFilters) {
    const txProperties = {
      "__DATA_UNIT_ADAPTER__[dataUnitName]": dataUnit.name
    };
    if (serverSideFilters.length !== 0) {
      txProperties["__DATA_UNIT_ADAPTER__[criteria]"] = JSON.stringify(serverSideFilters);
    }
    if (sorting != undefined && sorting.length !== 0) {
      txProperties["__DATA_UNIT_ADAPTER__[sorting]"] = JSON.stringify(sorting);
    }
    if (request.parentRecordId != undefined) {
      txProperties["__DATA_UNIT_ADAPTER__[parentRecordId]"] = request.parentRecordId;
    }
    const loaderProps = dataUnit.getGlobalLoaderProps();
    if (loaderProps == undefined || loaderProps.size === 0) {
      return txProperties;
    }
    Array.from(loaderProps.entries()).forEach(([name, value]) => {
      txProperties[name] = value;
    });
    return txProperties;
  }
  processRecords(dataUnit, fields, responseRecords) {
    return responseRecords.map((dataFrame) => {
      const lastElement = dataFrame[dataFrame.length - 1];
      const duRecord = {
        __record__id__: dataFrame[0],
        __record__label__: dataFrame[1],
        __record__metadata__: lastElement['_rmd'],
      };
      dataUnit.metadata.fields.forEach(fieldDescriptor => {
        duRecord[fieldDescriptor.name] = this.buildFieldValue(fieldDescriptor, fields, dataFrame);
      });
      return duRecord;
    });
  }
  buildFieldValue(descriptor, allFields, dataFrame) {
    const fieldIndex = allFields.indexOf(descriptor.name);
    if (fieldIndex < 0) {
      return null;
    }
    const strValue = dataFrame[fieldIndex];
    if (StringUtils.isEmpty(strValue)) {
      return null;
    }
    const descriptionField = this.getSearchDescriptionField(descriptor);
    if (descriptionField != undefined) {
      const descriptionFieldIndex = allFields.indexOf(descriptionField);
      if (descriptionFieldIndex >= 0) {
        return { value: strValue, label: dataFrame[descriptionFieldIndex] };
      }
    }
    if (descriptor.userInterface === UserInterface.SEARCH) {
      //Se não tem um campo de descrição (código acima)
      //Mas é um search, tentamos parsear um numero...
      return isNaN(Number(strValue)) ? strValue : Number(strValue);
    }
    return InMemoryLoader.getConvertedValue(descriptor, strValue);
  }
  getSearchDescriptionField(descriptor) {
    if (![UserInterface.SEARCH, UserInterface.SEARCHPLUS].includes(descriptor.userInterface)) {
      return undefined;
    }
    if (!descriptor.properties) {
      return undefined;
    }
    const { ENTITYNAME, mergedFrom, DESCRIPTIONFIELD } = descriptor.properties;
    if (!StringUtils.isEmpty(DESCRIPTIONFIELD)) {
      return mergedFrom != undefined ? `${mergedFrom}.${ENTITYNAME}.${DESCRIPTIONFIELD}` : `${ENTITYNAME}.${DESCRIPTIONFIELD}`;
    }
    return undefined;
  }
}
