import { DataUnitInMemoryLoader, ObjectUtils, DataUnit, ApplicationContext, StringUtils, UserInterface, DataUnitStorage, DataType, ChangeOperation } from '@sankhyalabs/core';
import { D as DataFetcher, S as SnkMessageBuilder, d as dist } from './DataFetcher.js';
import { DISTINCT_FILTER_NAME_PREFIX } from '@sankhyalabs/ezui/dist/collection/utils/constants';
import { DataUnitTransient } from '@sankhyalabs/core/dist/dataunit/metadata/UnitMetadata';
import { ColumnFilterManager } from '@sankhyalabs/ezui/dist/collection/components/ez-grid/utils';
import SortingUtils from '@sankhyalabs/core/dist/utils/SortingUtils';
import { R as ResourceIDUtils } from './ResourceIDUtils.js';

class InMemoryLoader {
  constructor(metadata, records, config) {
    this._dataUnitInMemoryLoader = new DataUnitInMemoryLoader(metadata, records, config);
  }
  get dataUnit() {
    return this._dataUnitInMemoryLoader.dataUnit;
  }
  get records() {
    return this._dataUnitInMemoryLoader.records;
  }
  set records(records) {
    this._dataUnitInMemoryLoader.records = records;
  }
  get metadata() {
    return this._dataUnitInMemoryLoader.metadata;
  }
  set metadata(metadata) {
    this._dataUnitInMemoryLoader.metadata = metadata;
  }
  removeLoader(_dataUnit, recordIds) {
    return this._dataUnitInMemoryLoader.removeLoader(_dataUnit, recordIds);
  }
  static getConvertedValue(descriptor, strValue, dateFormat) {
    return DataUnitInMemoryLoader.getConvertedValue(descriptor, strValue, dateFormat);
  }
}
InMemoryLoader.IN_MEMORY_DATA_UNIT_NAME = DataUnitInMemoryLoader.IN_MEMORY_DATA_UNIT_NAME;

class ArrayRepository {
  constructor(equalsFunction) {
    this._list = [];
    this._listCopy = [];
    this._listLastUpdate = 0;
    this._listCopyLastUpdate = 0;
    this._equalsFunction = equalsFunction;
  }
  async load(filterFunction, sortingFunction, offset, limit) {
    let result = [].concat(this._list);
    if (filterFunction != undefined) {
      result = this._list.filter(item => filterFunction(item));
    }
    if (sortingFunction != undefined) {
      result = result.sort(sortingFunction);
    }
    const count = result.length;
    if (limit != undefined) {
      const start = offset || 0;
      const end = limit ? start + limit : result.length;
      result = result.slice(start, end);
    }
    return Promise.resolve({ result, count });
  }
  async distict(itemProcessor) {
    const processedItems = [];
    let hasEmpty = false;
    for (const item of this._list) {
      const processedItem = itemProcessor(item);
      if (processedItem == undefined) {
        continue;
      }
      if (processedItem.value == undefined) {
        hasEmpty = true;
        continue;
      }
      processedItems.push(processedItem);
    }
    if (hasEmpty) {
      processedItems.push({ key: "", value: null });
    }
    return Promise.resolve(new Map(processedItems.map(item => [item.key, item.value])));
  }
  async push(items) {
    this.updateList([...this._list, ...items]);
  }
  async clear() {
    this.updateList([]);
  }
  async delete(items) {
    const list = this._list.filter(item => {
      for (const removed of items) {
        if (this._equalsFunction(item, removed)) {
          return false;
        }
      }
      return true;
    });
    this.updateList(list);
  }
  async update(items) {
    const list = this._list.map(existingItem => {
      const newItem = items.find(newItem => this._equalsFunction(existingItem, newItem));
      return newItem == undefined ? existingItem : newItem;
    });
    this.updateList(list);
  }
  async insert(itemReference, items) {
    const itemPosition = this._list.indexOf(itemReference);
    if (itemPosition == -1) {
      this.push(items);
      return;
    }
    const list = this._list.slice(0, itemPosition).concat(items).concat(this._list.slice(itemPosition));
    this.updateList(list);
  }
  isOperating() {
    return true;
  }
  async isEmpty() {
    return Promise.resolve(this._list.length === 0);
  }
  async count() {
    return Promise.resolve(this._list.length);
  }
  getFromCache() {
    if (this._listLastUpdate > this._listCopyLastUpdate) {
      this._listCopyLastUpdate = this._listLastUpdate;
      this._listCopy = ObjectUtils.copy(this._list);
    }
    return this._listCopy;
  }
  updateList(newList) {
    this._list = newList;
    this._listLastUpdate = Date.now();
  }
}

class PreloadManager {
  static setLoadingStatus(dataUnit, loadingInProgress) {
    this._loadingStatus.set(dataUnit.name, loadingInProgress);
  }
  static isCacheEnabled(_dataUnit) {
    return true;
  }
  static cacheRecords(dataUnit, records, resetDatabase, loadingInProgress) {
    PreloadManager.setLoadingStatus(dataUnit, loadingInProgress);
    if (PreloadManager.isCacheEnabled(dataUnit)) {
      this.getRepository(dataUnit).push(records);
    }
    else {
      if (resetDatabase) {
        this._repositories.delete(dataUnit.name);
      }
    }
  }
  static getSortingFunction(dataUnit, sorting) {
    return SortingUtils.getSortingFunction(dataUnit, sorting);
  }
  static async getDistinct(dataUnit, fieldName, onlyLabel = false) {
    if (!PreloadManager.isCacheEnabled(dataUnit)) {
      return Promise.resolve(ColumnFilterManager.compileDistinct(fieldName, dataUnit));
    }
    let filterFunction;
    const request = dataUnit.getLastLoadRequest();
    if (request != undefined) {
      const columnFilters = ColumnFilterManager.getColumnFilters(request.filters, fieldName);
      filterFunction = ColumnFilterManager.getFilterFunction(dataUnit, Array.from(columnFilters.values()));
    }
    if (onlyLabel) {
      return Promise.resolve(this.compileDistinctByLabel(dataUnit, fieldName, filterFunction));
    }
    return new Promise((accept, reject) => {
      PreloadManager.getRepository(dataUnit).distict(record => {
        if (filterFunction != undefined && !filterFunction(record)) {
          return undefined;
        }
        const fieldValue = record[fieldName];
        if (fieldValue == undefined) {
          return { key: null, value: null };
        }
        let value = fieldValue.value != undefined ? fieldValue.value : fieldValue;
        return { key: dataUnit.getFormattedValue(fieldName, fieldValue), value };
      })
        .then(result => {
        if (result == undefined) {
          accept(undefined);
          return;
        }
        accept(Array.from(result.entries()).map(([label, value]) => ({ label, value, check: true })));
      })
        .catch(reason => reject(reason));
    });
  }
  static compileDistinctByLabel(dataUnit, fieldName, filterFunction) {
    const groupedOptions = new Map();
    const cachedRecords = PreloadManager.getRepository(dataUnit).getFromCache();
    cachedRecords.forEach(record => {
      if (filterFunction != undefined && !filterFunction(record)) {
        return;
      }
      const fieldValue = record[fieldName];
      if (fieldValue == undefined) {
        const nullKey = '';
        if (!groupedOptions.has(nullKey)) {
          groupedOptions.set(nullKey, {
            values: new Set([null]),
            option: { label: null, value: null, check: true },
          });
        }
        return;
      }
      const value = fieldValue.value != undefined ? fieldValue.value : fieldValue;
      const label = (fieldValue === null || fieldValue === void 0 ? void 0 : fieldValue.label) ? fieldValue.label : dataUnit.getFormattedValue(fieldName, fieldValue);
      const key = String(label !== null && label !== void 0 ? label : '');
      let grouped = groupedOptions.get(key);
      if (!grouped) {
        grouped = {
          values: new Set(),
          option: { label, value, check: true },
        };
        groupedOptions.set(key, grouped);
      }
      grouped.values.add(value);
    });
    return Array.from(groupedOptions.values()).map(grouped => {
      const option = grouped.option;
      option.__multiValues = Array.from(grouped.values.values());
      return grouped.option;
    });
  }
  static async loadData(dataUnit, request, loadFromServer) {
    try {
      if (PreloadManager.isCacheEnabled(dataUnit)) {
        const useCache = [
          "EZ_GRID_LOADING_SOURCE",
          DataUnit.CHANGING_PAGE_LOADING_SOURCE,
          DataUnit.ALL_RECORDS_SELECTION_SOURCE
        ].includes(request.source);
        if (useCache) {
          const isCacheEmpty = await PreloadManager.getRepository(dataUnit).isEmpty();
          if (!isCacheEmpty) {
            return PreloadManager.loadFromCache(dataUnit, request);
          }
        }
        //Como não vamos aproveitar o cache, ele precisa ser limpo.
        PreloadManager.getRepository(dataUnit).clear().catch(() => { });
      }
      return loadFromServer(dataUnit, request);
    }
    catch (error) {
      console.error(error);
      return Promise.reject(error);
    }
  }
  static insertRecords(dataUnit, reference, records) {
    if (PreloadManager.isCacheEnabled(dataUnit)) {
      PreloadManager.getRepository(dataUnit).insert(reference, records);
    }
  }
  static updateRecords(dataUnit, records) {
    if (PreloadManager.isCacheEnabled(dataUnit)) {
      PreloadManager.getRepository(dataUnit).update(records);
    }
  }
  static removeRecords(dataUnit, records) {
    if (PreloadManager.isCacheEnabled(dataUnit)) {
      PreloadManager.getRepository(dataUnit).delete(records);
    }
  }
  static async countRecords(dataUnit) {
    if (PreloadManager.isCacheEnabled(dataUnit)) {
      return PreloadManager.getRepository(dataUnit).count();
    }
    return Promise.resolve(0);
  }
  static getRepository(dataUnit) {
    const name = dataUnit.name;
    if (!PreloadManager._repositories.has(name)) {
      PreloadManager._repositories.set(name, new ArrayRepository((recordA, recordB) => recordA.__record__id__ === recordB.__record__id__));
    }
    return PreloadManager._repositories.get(name);
  }
  static async loadFromCache(dataUnit, request) {
    return new Promise((accept, reject) => {
      const columnFilters = ColumnFilterManager.getColumnFilters(request.filters, "");
      const { limit, offset, sort } = request;
      PreloadManager.getRepository(dataUnit)
        .load(ColumnFilterManager.getFilterFunction(dataUnit, Array.from(columnFilters.values())), PreloadManager.getSortingFunction(dataUnit, sort), offset, limit)
        .then(loadResult => {
        const stillLoading = PreloadManager._loadingStatus.get(dataUnit.name);
        const { count, result: records } = loadResult;
        const firstRecord = count == 0 ? 0 : offset + 1;
        const lastRecord = offset + Math.min(records.length, limit);
        const currentPage = offset / limit;
        const paginationInfo = {
          count, currentPage, firstRecord, lastRecord,
          hasMore: stillLoading || (lastRecord < count),
          total: stillLoading ? undefined : count
        };
        accept({ records, paginationInfo });
      })
        .catch(reason => reject(reason));
    });
  }
  static getCachedRecords(dataUnit) {
    return PreloadManager.getRepository(dataUnit).getFromCache();
  }
}
PreloadManager._repositories = new Map();
PreloadManager._loadingStatus = new Map();

function getRecordValue(record, fieldName) {
  var _a, _b;
  return ((_a = record[fieldName]) === null || _a === void 0 ? void 0 : _a.value) !== undefined ? (_b = record[fieldName]) === null || _b === void 0 ? void 0 : _b.value : record[fieldName];
}

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

async function showAskCancelPaginationPopup() {
  return new Promise((resolve) => {
    const messagesBuilder = new SnkMessageBuilder();
    const { checkboxContainer, checkbox } = buildCheckboxContainer(messagesBuilder);
    const popup = builPopup(resolve, checkbox, messagesBuilder);
    const contentContainer = buildContentContainer();
    const actionsContainer = buildActionsContainer();
    contentContainer.appendChild(checkboxContainer);
    contentContainer.appendChild(buildMessage(messagesBuilder));
    actionsContainer.appendChild(buildContinuePaginationBtn(popup, checkbox, resolve, messagesBuilder));
    actionsContainer.appendChild(buildCancelPaginationBtn(popup, resolve, messagesBuilder));
    contentContainer.appendChild(actionsContainer);
    popup.appendChild(contentContainer);
    document.body.appendChild(popup);
  });
}
function buildCheckboxContainer(messagesBuilder) {
  const checkboxContainer = document.createElement('div');
  checkboxContainer.style.marginBottom = '14px';
  checkboxContainer.style.display = 'flex';
  checkboxContainer.style.alignItems = 'center';
  const { checkbox, checkboxLabel } = buildCheckbox(messagesBuilder);
  checkboxContainer.appendChild(checkbox);
  checkboxContainer.appendChild(checkboxLabel);
  return { checkboxContainer, checkbox };
}
function buildCheckbox(messagesBuilder) {
  const checkbox = document.createElement('input');
  checkbox.type = 'checkbox';
  checkbox.id = 'loadAll';
  const checkboxLabel = document.createElement('label');
  checkboxLabel.htmlFor = 'loadAll';
  checkboxLabel.textContent = messagesBuilder === null || messagesBuilder === void 0 ? void 0 : messagesBuilder.getMessage("dataUnitDataLoaderHelper.checkbox.loadAll");
  checkboxLabel.style.marginLeft = '5px';
  return { checkbox, checkboxLabel };
}
function buildActionsContainer() {
  const actionsContainer = document.createElement('div');
  actionsContainer.style.display = 'flex';
  actionsContainer.style.flexDirection = 'row';
  actionsContainer.style.justifyContent = 'end';
  actionsContainer.style.gap = '5px';
  return actionsContainer;
}
function buildContentContainer() {
  const contentContainer = document.createElement('div');
  contentContainer.style.display = 'flex';
  contentContainer.style.flexDirection = 'column';
  contentContainer.style.fontFamily = 'var(--font-pattern, Roboto)';
  contentContainer.style.fontSize = 'var(--text--medium)';
  return contentContainer;
}
function buildMessage(messagesBuilder) {
  const message = document.createElement('div');
  const line1 = document.createElement('p');
  const line2 = document.createElement('p');
  const line3 = document.createElement('p');
  line3.style.fontWeight = 'var(--text-weight--large, 600)';
  line1.textContent = messagesBuilder === null || messagesBuilder === void 0 ? void 0 : messagesBuilder.getMessage("dataUnitDataLoaderHelper.pagination.line1");
  line2.textContent = messagesBuilder === null || messagesBuilder === void 0 ? void 0 : messagesBuilder.getMessage("dataUnitDataLoaderHelper.pagination.line2");
  line3.textContent = messagesBuilder === null || messagesBuilder === void 0 ? void 0 : messagesBuilder.getMessage("dataUnitDataLoaderHelper.pagination.line3");
  message.appendChild(line1);
  message.appendChild(line2);
  message.appendChild(line3);
  return message;
}
function buildCancelPaginationBtn(popup, resolve, messagesBuilder) {
  const cancelPaginationBtn = document.createElement('ez-button');
  cancelPaginationBtn.label = messagesBuilder === null || messagesBuilder === void 0 ? void 0 : messagesBuilder.getMessage("dataUnitDataLoaderHelper.buttons.cancel");
  cancelPaginationBtn.size = 'medium';
  cancelPaginationBtn.classList.add('ez-button--primary');
  cancelPaginationBtn.onclick = () => {
    document.body.removeChild(popup);
    resolve(CancelPaginationResponse.CANCEL);
  };
  return cancelPaginationBtn;
}
function buildContinuePaginationBtn(popup, checkBox, resolve, messagesBuilder) {
  const continuePaginationBtn = document.createElement('ez-button');
  continuePaginationBtn.label = messagesBuilder === null || messagesBuilder === void 0 ? void 0 : messagesBuilder.getMessage("dataUnitDataLoaderHelper.buttons.continue");
  continuePaginationBtn.size = 'medium';
  continuePaginationBtn.onclick = () => {
    document.body.removeChild(popup);
    resolve(checkBox.checked ? CancelPaginationResponse.LOAD_ALL : CancelPaginationResponse.CONTINUE);
  };
  return continuePaginationBtn;
}
function builPopup(resolve, checkBox, messagesBuilder) {
  const popup = document.createElement('ez-popup');
  popup.opened = true;
  popup.size = 'x-small';
  popup.ezTitle = messagesBuilder === null || messagesBuilder === void 0 ? void 0 : messagesBuilder.getMessage("dataUnitDataLoaderHelper.pagination.title");
  popup.heightMode = "auto";
  popup.addEventListener('ezClosePopup', () => {
    document.body.removeChild(popup);
    resolve(checkBox.checked ? CancelPaginationResponse.LOAD_ALL : CancelPaginationResponse.CONTINUE);
  }, { once: true });
  popup.addEventListener('ezPopupAction', () => {
    document.body.removeChild(popup);
    resolve(CancelPaginationResponse.CANCEL);
  }, { once: true });
  return popup;
}
var CancelPaginationResponse;
(function (CancelPaginationResponse) {
  CancelPaginationResponse["CANCEL"] = "CANCEL";
  CancelPaginationResponse["CONTINUE"] = "CONTINUE";
  CancelPaginationResponse["LOAD_ALL"] = "LOAD_ALL";
})(CancelPaginationResponse || (CancelPaginationResponse = {}));

class DataUnitDataLoader {
  constructor() { }
  static async debounce(dataUnit, callBack) {
    const duName = dataUnit.name;
    if (DataUnitDataLoader._debouncingTimeouts[duName]) {
      clearTimeout(DataUnitDataLoader._debouncingTimeouts[duName]);
      delete DataUnitDataLoader._debouncingTimeouts[duName];
    }
    DataUnitDataLoader._debouncingTimeouts[duName] = setTimeout(() => {
      delete DataUnitDataLoader._debouncingTimeouts[duName];
      callBack();
    }, 100);
  }
  static async loadData(dataUnit, request) {
    return new Promise((resolve, reject) => {
      DataUnitDataLoader.debounce(dataUnit, () => {
        PreloadManager.loadData(dataUnit, request, this.loadFromServer)
          .then(resp => resolve(resp))
          .catch(reason => reject(reason));
      });
    });
  }
  static getCachedRecords(dataUnit) {
    return PreloadManager.getCachedRecords(dataUnit);
  }
  static async loadFromServer(dataUnit, request, loadingInfo) {
    try {
      //Registramos a request com as informações de carga para determinarmos a última.
      //Caso o usuário inicie outro refresh antes de terminar, devemos ignorar o resultado.
      loadingInfo = DataUnitDataLoader.registryLoading(dataUnit, loadingInfo);
      const loadResult = await DataUnitDataLoader.callLoader(dataUnit, request, loadingInfo);
      if (loadResult == undefined) {
        //Essa request foi originada de refresh antigo. Será ignorada;
        return;
      }
      const loadedRecords = loadResult.records;
      const paginationInfo = DataUnitDataLoader.buildPaginationInfo(loadedRecords.length, request, loadResult.loadingInfo);
      const records = paginationInfo == undefined ? loadedRecords : loadedRecords.slice(0, paginationInfo.lastRecord);
      return Promise.resolve({ records, paginationInfo });
    }
    catch (error) {
      console.error(error);
      return Promise.reject(error);
    }
  }
  static async callLoader(dataUnit, request, requestLoadingInfo, dataLoader) {
    if (dataLoader == undefined) {
      dataLoader = new DatasetStrategy();
    }
    const result = await dataLoader.load(dataUnit, request, requestLoadingInfo);
    const responseLoadingInfo = result.loadingInfo;
    if (DataUnitDataLoader.isOldRequest(dataUnit, responseLoadingInfo))
      return Promise.resolve(undefined);
    this.handleCache(dataLoader, responseLoadingInfo, dataUnit, result.records);
    if (dataUnit.cancelPagination)
      return this.handlePaginationCanceled(dataUnit, result);
    //Não existem mais páginas a serem carregadas
    if (this.canFinishPagination(dataUnit, responseLoadingInfo))
      return Promise.resolve(result);
    const newRequest = Object.assign(Object.assign({}, request), { offset: responseLoadingInfo.count });
    let newLoadingInfo = this.buildNewLoadingInfo(responseLoadingInfo);
    if (this.canProceedPagination(responseLoadingInfo))
      return this.recallLoader(dataUnit, newRequest, newLoadingInfo, dataLoader, result);
    const confirm = await showAskCancelPaginationPopup();
    if (confirm === CancelPaginationResponse.CANCEL)
      return this.handlePaginationCanceled(dataUnit, result);
    if (confirm === CancelPaginationResponse.LOAD_ALL) {
      newLoadingInfo = Object.assign(Object.assign({}, newLoadingInfo), { loadAllRecords: true });
    }
    newLoadingInfo = Object.assign(Object.assign({}, newLoadingInfo), { lastRowLimitAsked: newLoadingInfo.count });
    return this.recallLoader(dataUnit, newRequest, newLoadingInfo, dataLoader, result);
  }
  static buildNewLoadingInfo(responseLoadingInfo) {
    let newLoadingInfo = Object.assign(Object.assign({}, responseLoadingInfo), { pageNumber: (responseLoadingInfo.pageNumber || 0) + 1, quiet: true });
    return newLoadingInfo;
  }
  static handleCache(dataLoader, responseLoadingInfo, dataUnit, records) {
    const recreateCache = !dataLoader.canSlice() || responseLoadingInfo.count === 0;
    PreloadManager.cacheRecords(dataUnit, records, recreateCache, responseLoadingInfo.loadingInProgress);
  }
  static recallLoader(dataUnit, newRequest, newLoadingInfo, dataLoader, result) {
    this.callLoader(dataUnit, newRequest, newLoadingInfo, dataLoader)
      .then(result => DataUnitDataLoader.afterLoadingPage(dataUnit, result.loadingInfo))
      .catch(reason => console.error(reason));
    return Promise.resolve(result);
  }
  static canFinishPagination(dataUnit, responseLoadingInfo) {
    return !PreloadManager.isCacheEnabled(dataUnit) || !responseLoadingInfo.loadingInProgress;
  }
  static canProceedPagination(info) {
    const { count, askRowsLimit, loadAllRecords, lastRowLimitAsked } = info;
    if (loadAllRecords)
      return true;
    if (!count || !askRowsLimit)
      return true;
    const askRowLimitNumber = Number(askRowsLimit);
    if (count < askRowLimitNumber)
      return true;
    const nextLimitToAsk = askRowLimitNumber + (lastRowLimitAsked ? lastRowLimitAsked : 0);
    return count < nextLimitToAsk;
  }
  static handlePaginationCanceled(dataUnit, result) {
    dataUnit.cancelPagination = false;
    let loadingInfo = result.loadingInfo;
    loadingInfo = Object.assign(Object.assign({}, loadingInfo), { total: loadingInfo.count, loadingInProgress: false, pagerId: undefined, needReload: loadingInfo.hasLocalSorting });
    const loadDataResult = { records: result.records, loadingInfo };
    return Promise.resolve(loadDataResult);
  }
  static afterLoadingPage(dataUnit, loadingInfo) {
    PreloadManager.setLoadingStatus(dataUnit, loadingInfo.loadingInProgress);
    const dataUnitPagination = dataUnit.getPaginationInfo();
    if (dataUnitPagination == undefined) {
      return;
    }
    const count = loadingInfo.count;
    if (loadingInfo.loadingInProgress) {
      dataUnit.updatePagination(Object.assign(Object.assign({}, dataUnitPagination), { count, loadingInProgress: true }));
      return;
    }
    if (loadingInfo.needReload) {
      //Ir para a primeira página, faz com que o loadData seja chamado novamente
      dataUnit.gotoPage(0);
      return;
    }
    dataUnit.updatePagination(Object.assign(Object.assign({}, dataUnitPagination), { total: count, count, loadingInProgress: false }));
  }
  static registryLoading(dataUnit, loadingInfo) {
    if (loadingInfo == undefined) {
      loadingInfo = { requestTime: new Date().getTime(), count: 0 };
    }
    DataUnitDataLoader._requestTimeByDataUnit.set(dataUnit.name, loadingInfo.requestTime);
    return loadingInfo;
  }
  static isOldRequest(dataUnit, loadingInfo) {
    return DataUnitDataLoader._requestTimeByDataUnit.get(dataUnit.name) > loadingInfo.requestTime;
  }
  static buildPaginationInfo(pageSize, request, loadingInfo) {
    let { limit, offset } = request;
    if (!limit) {
      return undefined;
    }
    if (loadingInfo.pageNumber === 1) {
      // Quando o loader não é capaz de fatiar os registros
      // ele pode decidir por uma carga completa, o que faz o
      // controle de paginação voltar pra primeira página.
      offset = 0;
    }
    const { total, count, loadingInProgress, askRowsLimit } = loadingInfo;
    const firstRecord = (count === 0 || pageSize === 0) ? 0 : offset + 1;
    const lastRecord = offset + Math.min(pageSize, limit);
    return {
      total,
      count,
      lastRecord,
      firstRecord,
      currentPage: offset / limit,
      hasMore: lastRecord < count || loadingInProgress,
      askRowsLimit
    };
  }
}
DataUnitDataLoader._requestTimeByDataUnit = new Map();
DataUnitDataLoader._debouncingTimeouts = {};

var __rest = (undefined && undefined.__rest) || function (s, e) {
  var t = {};
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
    t[p] = s[p];
  if (s != null && typeof Object.getOwnPropertySymbols === "function")
    for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
      if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
        t[p[i]] = s[p[i]];
    }
  return t;
};
const DATA_UNIT_NAME_PARSER = /dd:\/\/([^/]+)\/([^/?]+)/;
class DataUnitFetcher {
  constructor() {
    this.templateByQuery = new Map();
    this.buldTemplates();
  }
  buldTemplates() {
    this.templateByQuery.set("fetchDataUnit", dist.gql `query($name: String!) {
              $queryAlias$: fetchDataUnit(name: $name){
                name
                fields{
                  name
                  defaultValue
                  label
                  visible
                  standAlone
                  readOnly
                  required
                  dataType
                  userInterface
                  calculated
                  group
                  order
                  properties{
                    name
                    value
                  }
                  dependencies{
                    masterFields
                    type
                    expression
                  }
                }
                children{
                  name
                  label
                  links{
                    source
                    target
                  }
                }
              }
            }`);
    this.templateByQuery.set("saveData", dist.gql `mutation($changes: [InputBatchChange!]!) {
            $queryAlias$: batchOperationDataUnit(changes: $changes){
              oldId
              id
              label
              ownerDataUnitName
              fields {
                name
                value
              }
            }
          }`);
    this.templateByQuery.set("fetchDataRecord", dist.gql `query($dataunit: String! $recordID: [String!]) {
            $queryAlias$: fetchDataUnit(name: $dataunit){
              record(id: $recordID){
                id
                label
                fields {
                  name
                  value
                }
              }
            }
          }`);
    this.templateByQuery.set("fetchDistinctColumn", dist.gql `query($dataUnit: String!, $fieldName: String!, $argument: String, $filters: [InputFilter], $parentRecordId: String) {
          $queryAlias$: selectDistinct(dataUnit: $dataUnit, fieldName: $fieldName, argument: $argument, filters: $filters, parentRecordId: $parentRecordId)
      }`);
  }
  static parseDataUnitName(name) {
    if (name == undefined) {
      return undefined;
    }
    const parts = DATA_UNIT_NAME_PARSER.exec(name);
    if (!parts) {
      return undefined;
    }
    return { entityName: parts[1], resourceID: parts[2] };
  }
  getDataUnit(entityName, resourceID, parentDataUnit, configName) {
    if (PreloadManager.applicationResourceID == undefined) {
      //Por falta de um lugar melhor pra iniciar essa variável, fazemos isso aqui.
      ResourceIDUtils.getResourceID().then(resourceID => PreloadManager.applicationResourceID = resourceID);
    }
    const duName = `dd://${entityName}/${resourceID}${configName ? "/" + configName : ""}`;
    const dataUnit = parentDataUnit != undefined ? parentDataUnit.getChildDataunit(duName) : new DataUnit(duName);
    dataUnit.metadataLoader = (dataUnit) => this.loadMetadata(dataUnit);
    dataUnit.dataLoader = (dataUnit, request) => DataUnitDataLoader.loadData(dataUnit, request);
    dataUnit.saveLoader = (_dataUnit, changes) => this.saveData(dataUnit, changes);
    dataUnit.removeLoader = (dataUnit, recordIds) => this.removeRecords(dataUnit, recordIds);
    dataUnit.recordLoader = (dataUnit, recordIds) => this.loadRecord(dataUnit, recordIds);
    dataUnit.allRecordsLoader = (dataUnit) => DataUnitDataLoader.getCachedRecords(dataUnit);
    return dataUnit;
  }
  loadMetadata(dataUnit) {
    return new Promise((resolve, reject) => {
      DataFetcher.get()
        .callGraphQL({
        values: { name: dataUnit.name },
        query: this.templateByQuery.get("fetchDataUnit"),
      })
        .then((resp) => {
        var _a;
        const metadata = {
          name: resp.name,
          label: resp.name,
          children: [...resp.children],
          fields: []
        };
        (_a = resp.fields) === null || _a === void 0 ? void 0 : _a.forEach((source) => {
          let properties = undefined;
          if (Array.isArray(source.properties)) {
            properties = {};
            if (source.calculated) {
              properties.gridHeaderTooltip = "Campos calculados não podem ser ordenados";
            }
            source.properties.forEach((prop) => (properties[prop.name] = prop.value));
          }
          metadata.fields.push(Object.assign(Object.assign({}, source), { properties }));
        });
        metadata.fields.sort((a, b) => a.order - b.order);
        resolve(metadata);
      })
        .catch((error) => {
        reject(error);
      });
    });
  }
  loadSelectDistinct(dataUnit, fieldName, argument) {
    const { parentRecordId, filters } = dataUnit.getLastLoadRequest() || {};
    const filteredFilters = filters.filter(f => f.name !== `${DISTINCT_FILTER_NAME_PREFIX}${fieldName}`);
    const values = {
      dataUnit: dataUnit.name,
      argument,
      fieldName,
      parentRecordId,
      filters: filteredFilters
    };
    return new Promise((resolve, reject) => {
      DataFetcher.get()
        .callGraphQL({
        values,
        query: this.templateByQuery.get("fetchDistinctColumn"),
      })
        .then((result) => {
        resolve(result);
      })
        .catch((error) => {
        reject(error);
      });
    });
  }
  addTransientProperties(dataUnit, updatingFields) {
    const loaderProps = dataUnit.getGlobalLoaderProps();
    if (loaderProps == undefined || loaderProps.size === 0) {
      return updatingFields;
    }
    if (updatingFields == undefined) {
      updatingFields = {};
    }
    Array.from(loaderProps.entries()).forEach(([name, value]) => {
      updatingFields[`transient.${name}`] = value;
    });
    return updatingFields;
  }
  getUpdatingFields(dataUnit, originalUpdatingFields) {
    if (originalUpdatingFields == undefined) {
      return;
    }
    const updatingFields = Object.assign({}, originalUpdatingFields);
    Object.keys(updatingFields).forEach(key => {
      const descriptor = dataUnit.getField(key);
      if (descriptor != undefined && descriptor.standAlone) {
        delete updatingFields[key];
      }
    });
    return this.addTransientProperties(dataUnit, updatingFields);
  }
  saveData(dataUnit, duChanges) {
    const updatedRecordsIds = [];
    const addedRecordsIds = [];
    const changes = duChanges.map((change) => {
      const { dataUnit: changeDU, record, operation } = change;
      const dataUnitInstance = DataUnitStorage.get(changeDU);
      const updatingFields = this.getUpdatingFields(dataUnitInstance, change.updatingFields);
      let parsedUpdatingFields;
      if (updatingFields != undefined) {
        parsedUpdatingFields = Object.entries(updatingFields).map(([fieldName, value]) => {
          const descriptor = dataUnitInstance.getField(fieldName);
          const dataType = descriptor ? descriptor.dataType : DataType.TEXT;
          return { fieldName, dataType, value: dataUnitInstance.valueToString(fieldName, value) };
        });
      }
      if (changeDU === dataUnit.name) {
        if (operation === ChangeOperation.INSERT || operation === ChangeOperation.COPY) {
          addedRecordsIds.push(record.__record__id__);
        }
        else {
          updatedRecordsIds.push(record.__record__id__);
        }
      }
      const reqChange = { dataUnit: changeDU, fields: parsedUpdatingFields, operation, recordId: record.__record__id__ };
      if (change.sourceId) {
        reqChange.sourceId = change.sourceId;
      }
      if (record.__parent__record__id__) {
        reqChange.parentRecordId = record.__parent__record__id__;
      }
      return reqChange;
    });
    return new Promise((resolve, reject) => {
      const changesFormatted = changes.map((_a) => {
        var rest = __rest(_a, []);
        return rest;
      });
      DataFetcher.get()
        .callGraphQL({
        values: { changes: changesFormatted },
        query: this.templateByQuery.get("saveData"),
      })
        .then((resp) => {
        const dataUnitRecords = [];
        resp === null || resp === void 0 ? void 0 : resp.forEach((responseRecord) => {
          const duRecord = {
            __record__id__: responseRecord.id,
            __record__label__: responseRecord.label,
            __owner__dataunit__name__: responseRecord.ownerDataUnitName,
          };
          const dataUnitInstance = DataUnitStorage.get(duRecord.__owner__dataunit__name__) || dataUnit;
          if (responseRecord.oldId) {
            duRecord.__old__id__ = responseRecord.oldId;
          }
          responseRecord.fields.forEach(({ name, value }) => {
            var _a;
            const valueFromString = (_a = dataUnitInstance === null || dataUnitInstance === void 0 ? void 0 : dataUnitInstance.valueFromString) === null || _a === void 0 ? void 0 : _a.call(dataUnitInstance, name, value);
            duRecord[name] = (valueFromString !== undefined) ? valueFromString : value;
          });
          dataUnitRecords.push(duRecord);
        });
        this.updateCache(dataUnit, dataUnitRecords, updatedRecordsIds, addedRecordsIds);
        resolve(dataUnitRecords);
      })
        .catch((error) => {
        reject(error);
      });
    });
  }
  updateCache(dataUnit, savedRecords, toUpdate, toInsert) {
    const recordsById = new Map(savedRecords.map(record => [record.__old__id__ || record.__record__id__, record]));
    const addedRecords = toInsert.map(recordId => {
      const copy = Object.assign({}, recordsById.get(recordId));
      delete copy.__old__id__;
      return copy;
    });
    if (addedRecords.length > 0) {
      const firstRecord = dataUnit.records[0];
      PreloadManager.insertRecords(dataUnit, firstRecord, addedRecords);
    }
    const updatedRecords = toUpdate.map(recordId => (Object.assign({}, recordsById.get(recordId))));
    PreloadManager.updateRecords(dataUnit, updatedRecords);
  }
  getTransientInfo(dataUnit, recordID) {
    const { records } = dataUnit.getSelectionInfo();
    return Object.entries(records.filter(record => record.__record__id__ == recordID)[0]).filter(([key]) => key.startsWith(DataUnitTransient.DATA_UNIT_TRANSIENT_PREFIX_NAME)).map(([name, value]) => ({ fieldName: name, value, dataType: DataType.TEXT }));
  }
  removeRecords(dataUnit, recordIds) {
    const changes = recordIds.map((recordId) => {
      return { dataUnit: dataUnit.name, operation: ChangeOperation.DELETE, recordId, fields: this.getTransientInfo(dataUnit, recordId) };
    });
    return new Promise((resolve, reject) => {
      DataFetcher.get()
        .callGraphQL({
        values: { changes: changes },
        query: this.templateByQuery.get("saveData"),
      })
        .then((_resp) => {
        PreloadManager.removeRecords(dataUnit, dataUnit.records.filter(record => recordIds.includes(record.__record__id__)));
        resolve(recordIds);
      })
        .catch((error) => {
        reject(error);
      });
    });
  }
  loadRecord(dataUnit, recordIds) {
    return new Promise((resolve, reject) => {
      DataFetcher.get()
        .callGraphQL({
        values: { recordID: recordIds, dataunit: dataUnit.name },
        query: this.templateByQuery.get("fetchDataRecord"),
      })
        .then((response) => {
        const dataUnitRecords = [];
        response.record.forEach(responseRecord => {
          const duRecord = { __record__id__: responseRecord.id, __record__label__: responseRecord.label };
          responseRecord.fields.forEach(({ name, value }) => {
            duRecord[name] = dataUnit.valueFromString(name, value);
          });
          dataUnitRecords.push(duRecord);
        });
        //TODO: Chamar o worker para persistir a nova versão do registro
        resolve(dataUnitRecords);
      })
        .catch((error) => {
        reject(error);
      });
    });
  }
}

export { DataUnitFetcher as D, InMemoryLoader as I, PreloadManager as P, DatasetStrategy as a, getRecordValue as g };
