import { h, Host } from '@stencil/core';
import { Action, ApplicationContext, DataUnitAction, ObjectUtils, StringUtils, } from '@sankhyalabs/core';
import { ApplicationUtils, DialogType } from '@sankhyalabs/ezui/dist/collection/utils';
import { OperationMap, SnkMessageBuilder } from '../../lib/message/SnkMessageBuilder';
import { DatasetStrategy } from '../../lib/http/data-fetcher/fetchers/data-unit/loadstrategy/DatasetStrategy';
import { getRecordValue } from '../../lib/dataUnit/ValueFormatter';
import { convertType } from '@sankhyalabs/core/dist/dataunit/metadata/DataType';
import { getSelectedRecordsIDsInfo } from '../../lib/utils/GetSelectedRecordsIDsInfo';
import { showConfirmMultipleUpdatePopup } from './MultipleUpdateHelper';
export class SnkDataUnit {
  constructor() {
    this._onDataUnitResolve = [];
    this._openedAlert = false;
    this._fieldsWithRmp = [];
    this._fieldsWithRmPrecision = [];
    this._metadataByRow = new Map();
    this._rowMetadataCache = new Map();
    this._formFieldsConfig = [];
    this._internalDataStateChange = false;
    this.REGEX_DATAUNIT_NAME = /dd:\/\/(.+?)\//;
    this._dataUnitObserver = async (action) => {
      const duState = await this.buildDataState(action.type);
      this.dataState = duState;
      this.messagesBuilder.currentOperation = this.getMessageOperation();
      switch (action.type) {
        case Action.DATA_SAVED:
          await this.handleDataSaved(action);
          break;
        case Action.RECORDS_ADDED:
        case Action.RECORDS_COPIED:
          this.insertionMode.emit();
          break;
        case Action.EDITION_CANCELED:
          this.cancelEdition.emit();
          break;
        case Action.RECORDS_REMOVED:
          this.handleRecordsRemoved(action);
          break;
        case Action.METADATA_LOADED:
          this.fillFieldsWithRmp(this.dataUnit.metadata);
          this.fillFieldsWithRmPrecision(this.dataUnit.metadata);
          break;
        default:
          break;
      }
    };
    this.fieldsToLink = [];
    this.dataState = undefined;
    this.messagesBuilder = undefined;
    this.dataUnitName = undefined;
    this.entityName = undefined;
    this.pageSize = 150;
    this.dataUnit = undefined;
    this.beforeSave = undefined;
    this.afterSave = undefined;
    this.useCancelConfirm = true;
    this.ignoreSaveMessage = undefined;
    this.configName = undefined;
    this.resourceID = undefined;
    this.domainMessagesBuilder = undefined;
  }
  observePageSize() {
    if (this.dataUnit) {
      this.dataUnit.pageSize = this.pageSize;
    }
  }
  observeDataUnitName(newValue, oldValue) {
    if (oldValue != newValue) {
      if (this.dataUnit) {
        this._application.updateDataunitCache(oldValue, this.dataUnitName, this.dataUnit);
      }
      else {
        this.loadDataUnit();
      }
    }
  }
  observeEntityName(newValue, oldValue) {
    if (oldValue != newValue) {
      this.dataUnit = undefined;
      this.entityName = newValue;
      this.loadDataUnit();
    }
  }
  observeDataState(newValue, oldValue) {
    if (ObjectUtils.objectToString(oldValue) != ObjectUtils.objectToString(newValue)) {
      this.dataStateChange.emit(newValue);
    }
  }
  observeDataUnit() {
    this.handlerLinkFields();
    this.dataUnitReady.emit(this.dataUnit);
  }
  observeMessagesBuilder(newValue) {
    if (newValue) {
      this.messagesBuilderUpdated.emit(newValue);
    }
  }
  onMasterFormConfigChange({ detail }) {
    var _a;
    this._formFieldsConfig = (_a = detail === null || detail === void 0 ? void 0 : detail.fields) !== null && _a !== void 0 ? _a : [];
  }
  /**
   * Obtém o dataUnit.
   * @returns {Promise<DataUnit>} Uma promessa que resolve com a instância do DataUnit.
   * @async
   */
  async getDataUnit() {
    return new Promise((resolve) => {
      if (this.dataUnit) {
        resolve(this.dataUnit);
      }
      else {
        this._onDataUnitResolve.push(resolve);
      }
    });
  }
  /**
   * Método que retorna a lista de IDs dos registros selecionados.
   * @returns {Promise<Array<IRecordID>>} Retorna uma promessa que resolve com a lista de IDs dos registros selecionados.
   * @async
   */
  async getSelectedRecordsIDsInfo() {
    return getSelectedRecordsIDsInfo(this.dataUnit);
  }
  /**
   * Retorna os campos que possuem a propriedade "rmp" (Row Metadata Provider).
   * @returns {Promise<string[]>} Uma promessa que resolve com um array de nomes de campos.
   * @async
   */
  async getFieldsWithRmp() {
    return this._fieldsWithRmp;
  }
  /**
   * Retorna os campos que possuem a propriedade "rm_precision" (Row Metadata Precision).
   * @returns {Promise<string[]>} Uma promessa que resolve com um array de nomes de campos.
   * @async
   */
  async getFieldsWithRmPrecision() {
    return this._fieldsWithRmPrecision;
  }
  /**
   * Busca os metadados da linha selecionada.
   * @param {Record | string} [record] - O registro ou ID do registro para o qual buscar os metadados. Se omitido, usa o registro selecionado.
   * @returns {Promise<RowMetadata>} Uma promessa que resolve com os metadados da linha.
   * @async
   */
  async getRowMetadata(record) {
    return this.doGetRowMetadata(record);
  }
  async doGetRowMetadata(record) {
    const selectedRecord = this.dataUnit.getSelectedRecord();
    if (!record && !selectedRecord) {
      return;
    }
    const currentRecord = record || selectedRecord;
    const recordId = typeof currentRecord === 'string' ? currentRecord : currentRecord.__record__id__;
    let rowMetadata = this._metadataByRow.get(recordId);
    /*
      Nao fazemos cache de metadataByRow pra registros novos, pois a chave do mesmo
      muda ao ser salvo, por isso essa tratativa é necessária para que o dataState fique com metadados
      corretos nao confiando apenas no _metadataByRow
    */
    if (this.dataUnit.isNewRecord(recordId) && !rowMetadata) {
      rowMetadata = await this.handleLoadRowMetadata(selectedRecord);
    }
    if (!rowMetadata) {
      return;
    }
    rowMetadata.getProp = this.buildGetPropRowMetadata(rowMetadata);
    return Object.assign({}, rowMetadata);
  }
  buildGetPropRowMetadata(rowMetadata) {
    return (propPath, fieldName) => {
      if (propPath === "rm_precision") {
        propPath = this.resolvePrecisionFromMetadata(fieldName);
      }
      return ObjectUtils.getProp(rowMetadata, propPath);
    };
  }
  resolvePrecisionFromMetadata(fieldName) {
    var _a, _b;
    if (!fieldName) {
      ApplicationUtils.error(this.getMessage("snkDataUnit.fieldNameRequired"));
      return;
    }
    const rmPrecision = (_b = (_a = this.dataUnit.getField(fieldName)) === null || _a === void 0 ? void 0 : _a.properties) === null || _b === void 0 ? void 0 : _b.rm_precision;
    if (!rmPrecision) {
      return '';
    }
    let rmPrecisionPath = rmPrecision.split('.').filter((_, index) => index !== 1).join('.');
    return rmPrecisionPath;
  }
  getCleanOnCopyFields() {
    var _a;
    return (_a = this.dataUnit.metadata) === null || _a === void 0 ? void 0 : _a.fields.filter(field => { var _a; return (_a = field.properties) === null || _a === void 0 ? void 0 : _a.cleanOnCopy; }).map(field => field.name);
  }
  fillFieldsWithRmp(payload) {
    payload.fields.forEach((field) => {
      var _a;
      if (((_a = field.properties) === null || _a === void 0 ? void 0 : _a.rmp) && !this._fieldsWithRmp.includes(field.name)) {
        this._fieldsWithRmp.push(field.name);
      }
    });
  }
  fillFieldsWithRmPrecision(payload) {
    payload.fields.forEach((field) => {
      var _a;
      if (((_a = field.properties) === null || _a === void 0 ? void 0 : _a.rm_precision) && !this._fieldsWithRmPrecision.includes(field.name)) {
        this._fieldsWithRmPrecision.push(field.name);
      }
    });
  }
  getCacheName(fieldName, metadataName, fieldValue) {
    const cacheName = [fieldName, metadataName, fieldValue].join("_");
    return cacheName;
  }
  updateDataStateRmp(newValue) {
    const newRowMetadata = Object.assign({}, newValue);
    this.dataState.rowMetadata = newRowMetadata;
  }
  saveCacheRmd(record) {
    const rmd = record.__record__metadata__;
    const keysRmd = Object.keys(rmd);
    keysRmd.shift();
    keysRmd.forEach((fieldName) => {
      const fieldValue = getRecordValue(record, fieldName);
      const cacheName = this.getCacheName(fieldName, rmd.provider, fieldValue);
      this._rowMetadataCache.set(cacheName, Object.assign({}, rmd));
    });
  }
  getMetadataByRow(payload) {
    var _a;
    if (!((_a = payload.records) === null || _a === void 0 ? void 0 : _a.length)) {
      return payload;
    }
    if (!this._fieldsWithRmp.length) {
      const records = payload.records.map((record) => {
        delete record.__record__metadata__;
        return record;
      });
      return Object.assign(Object.assign({}, payload), { records });
    }
    const records = payload.records.map((record) => {
      if (!record.__record__metadata__) {
        return record;
      }
      this.saveCacheRmd(record);
      const rowMetadata = Object.assign({}, record.__record__metadata__);
      rowMetadata.getProp = this.buildGetPropRowMetadata(rowMetadata);
      this._metadataByRow.set(record.__record__id__, rowMetadata);
      delete record.__record__metadata__;
      return record;
    });
    return Object.assign(Object.assign({}, payload), { records });
  }
  interceptRecordsAdded(action) {
    if (!this.isAllowed("INSERT")) {
      ApplicationUtils.info(this.getMessage("snkDataUnit.forbiddenInsert"));
      return;
    }
    return new DataUnitAction(Action.RECORDS_ADDED, this.handleLinksWithParent(action.payload));
  }
  interceptRecordsCopied(action) {
    if (!this.isAllowed("CLONE")) {
      ApplicationUtils.info(this.getMessage("snkDataUnit.forbiddenClone"));
      return;
    }
    return new DataUnitAction(Action.RECORDS_COPIED, this.handleRecordsCopied(action.payload));
  }
  async interceptDataChange(action) {
    var _a;
    const recordId = (_a = this.dataUnit.getSelectedRecord()) === null || _a === void 0 ? void 0 : _a.__record__id__;
    if (this.isAllowed("UPDATE") || this.dataUnit.isNewRecord(recordId)) {
      return action;
    }
    if (this._internalDataStateChange) {
      this._internalDataStateChange = false;
      return;
    }
    if (this._openedAlert) {
      this._internalDataStateChange = true;
      await this.dataUnit.cancelEdition();
      return;
    }
    this._openedAlert = true;
    this._internalDataStateChange = true;
    await this.dataUnit.cancelEdition();
    await ApplicationUtils.alert(this.getMessage("snkDataUnit.forbidden"), this.getMessage("snkDataUnit.forbiddenUpdate"));
    this._openedAlert = false;
  }
  async interceptSavingData(action) {
    if (this.dataUnit.isMultipleEdition) {
      const confirm = await this.confirmMultipleSavingData();
      if (!confirm)
        return undefined;
    }
    if (!this.beforeSave)
      return action;
    const continueAction = this.beforeSave(this.dataUnit);
    if (continueAction instanceof Promise) {
      const result = await continueAction;
      return result ? action : undefined;
    }
    return continueAction ? action : undefined;
  }
  async confirmMultipleSavingData() {
    const selectedRecords = this.dataUnit.getSelectionInfo().records;
    const title = this.getMessage('snkDataUnit.multipleUpdateConfirmationTitle');
    const message = this.getMessage('snkDataUnit.multipleUpdateConfirmationMessage', { size: selectedRecords.length });
    const labelBtnCancel = this.getMessage('snkDataUnit.confirm.cancel');
    const labelBtnConfirm = this.getMessage('snkDataUnit.confirm.updateMultipleConfirm');
    return await showConfirmMultipleUpdatePopup({
      title,
      message,
      labelBtnConfirm,
      labelBtnCancel,
      changingFields: this.getChangingFields(),
    });
  }
  getChangingFields() {
    const changes = this.dataUnit.buildChangesToSave();
    if (!(changes === null || changes === void 0 ? void 0 : changes.length))
      return [];
    const change = changes[0];
    const changingFields = Object.keys(change.updatingFields).map(key => this.buildChangingField(key, change[key]));
    return changingFields;
  }
  buildChangingField(key, value) {
    const fieldLabel = this.dataUnit.getField(key).label;
    const formattedValue = this.dataUnit.getFormattedValue(key, value);
    return {
      label: fieldLabel,
      value: formattedValue,
      tabName: this.getFieldTabName(key),
    };
  }
  getFieldTabName(fieldName) {
    var _a;
    const tab = (_a = this._formFieldsConfig.find(f => f.name === fieldName)) === null || _a === void 0 ? void 0 : _a.tab;
    if (!tab)
      return undefined;
    if (typeof tab === 'string')
      return tab;
    return tab.label;
  }
  interceptDataSaved(action) {
    if (this.afterSave) {
      this.afterSave(this.dataUnit);
      return;
    }
    return action;
  }
  async interceptEditionCanceled(action) {
    var _a, _b;
    if (!this.useCancelConfirm || !this.dataState.hasDirtyRecords || ((_a = action.payload) === null || _a === void 0 ? void 0 : _a.fromParent) || ((_b = action.payload) === null || _b === void 0 ? void 0 : _b.silent)) {
      return action;
    }
    const cancelConfirmation = this.getMessage("snkDataUnit.cancelConfirmation");
    if (cancelConfirmation == undefined) {
      this.showSuccessMessage(this.getMessage("snkDataUnit.cancelInfo"));
      return action;
    }
    const cancelConfirmationTitle = this.getMessage("snkDataUnit.cancelConfirmationTitle");
    const confirm = await ApplicationUtils.confirm(cancelConfirmationTitle, cancelConfirmation);
    if (confirm) {
      let editionCanceledMessage;
      if (this.dataUnit.isMultipleEdition) {
        const selectedRecords = this.dataUnit.getSelectionInfo().records;
        editionCanceledMessage = this.buildMultipleUpdateMessage(selectedRecords, true);
      }
      else {
        editionCanceledMessage = this.getMessage("snkDataUnit.cancelInfo");
      }
      this.showSuccessMessage(editionCanceledMessage);
    }
    return confirm ? action : undefined;
  }
  async interceptRemovingRecords(action) {
    var _a, _b, _c;
    if (!this.isAllowed("REMOVE")) {
      ApplicationUtils.info(this.getMessage("snkDataUnit.forbiddenRemove"));
      return;
    }
    let multipleSelection = false;
    let removeConfirmation = !((_a = action.payload) === null || _a === void 0 ? void 0 : _a.silent) && this.getMessage("snkDataUnit.removeConfirmation");
    const selection = (_b = this.dataUnit) === null || _b === void 0 ? void 0 : _b.getSelectionInfo();
    if (!((_c = action.payload) === null || _c === void 0 ? void 0 : _c.silent) && (selection === null || selection === void 0 ? void 0 : selection.length) > 1) {
      removeConfirmation = this.getMessage("snkDataUnit.removeAllConfirmation", { size: selection.length });
      multipleSelection = true;
    }
    if (!removeConfirmation) {
      return action;
    }
    const options = {
      canClose: false,
      labelCancel: this.getMessage(`snkDataUnit.confirm.${multipleSelection ? "no" : "cancel"}`),
      labelConfirm: this.getMessage(`snkDataUnit.confirm.${multipleSelection ? "yes" : "delete"}`),
      btnConfirmDanger: false
    };
    const removeConfirmationTitle = this.getMessage("snkDataUnit.removeConfirmationTitle");
    const confirm = await ApplicationUtils.confirm(removeConfirmationTitle, removeConfirmation, null, DialogType.WARN, options);
    return confirm ? action : undefined;
  }
  interceptDataLoaded(action) {
    const newPayload = this.getMetadataByRow(action.payload);
    action = new DataUnitAction(Action.DATA_LOADED, newPayload);
    return action;
  }
  interceptMetadataLoaded(action) {
    this.fillFieldsWithRmp(action.payload);
    this.fillFieldsWithRmPrecision(action.payload);
    return new DataUnitAction(Action.METADATA_LOADED, this.handleDULinksOnMetadataLoaded(action.payload));
  }
  async interceptAction(action) {
    switch (action.type) {
      case Action.RECORDS_ADDED:
        return this.interceptRecordsAdded(action);
      case Action.RECORDS_COPIED:
        return this.interceptRecordsCopied(action);
      case Action.DATA_CHANGED:
        await this.handleLoadRowMetadata(action.payload);
        return await this.interceptDataChange(action);
      case Action.CHANGING_DATA:
        return await this.interceptDataChange(action);
      case Action.SAVING_DATA:
        return await this.interceptSavingData(action);
      case Action.DATA_SAVED:
        return this.interceptDataSaved(action);
      case Action.EDITION_CANCELED:
        return await this.interceptEditionCanceled(action);
      case Action.REMOVING_RECORDS:
        return await this.interceptRemovingRecords(action);
      case Action.DATA_LOADED:
        return this.interceptDataLoaded(action);
      case Action.METADATA_LOADED:
        return this.interceptMetadataLoaded(action);
      default:
        return action;
    }
  }
  async handleLoadRowMetadata(record) {
    if (!record)
      return;
    const keysPayload = Object.keys(record);
    const rmpField = keysPayload.find((field) => this._fieldsWithRmp.includes(field));
    if (rmpField) {
      const metadataName = this.dataUnit.getField(rmpField).properties.rmp;
      const fieldValue = getRecordValue(record, rmpField);
      return await this.updateRowMetadata({
        fieldName: rmpField,
        fieldValue,
        metadataName,
        updatedFields: record,
      });
    }
  }
  async updateRowMetadata({ fieldName, fieldValue, metadataName, updatedFields }) {
    const cacheName = this.getCacheName(fieldName, metadataName, fieldValue);
    const cacheValue = this._rowMetadataCache.get(cacheName);
    if (cacheValue) {
      this.updateDataStateRmp(cacheValue);
      return cacheValue;
    }
    else {
      const datasetStrategy = new DatasetStrategy();
      const response = await datasetStrategy.loadRowMetadata(this, fieldName, metadataName, updatedFields);
      this.updateDataStateRmp(response._rmd);
      this._rowMetadataCache.set(cacheName, Object.assign({}, response._rmd));
      return Object.assign({}, response._rmd);
    }
  }
  handleDULinksOnMetadataLoaded(payload) {
    const metadata = Object.assign({}, payload);
    this.processLinks(metadata);
    return metadata;
  }
  handleRecordsCopied(initialRecords) {
    let records = [...initialRecords];
    records = this.handleCleanOnCopy(records);
    return this.handleLinksWithParent(records);
  }
  handleLinksWithParent(records) {
    var _a, _b;
    const parentDataUnit = (_a = this._parentDataUnit) !== null && _a !== void 0 ? _a : (_b = this.dataUnit) === null || _b === void 0 ? void 0 : _b.getParentDataUnit();
    if (!parentDataUnit)
      return records;
    const parentSelectedRecord = parentDataUnit.getSelectedRecord();
    const selfInfo = parentDataUnit === null || parentDataUnit === void 0 ? void 0 : parentDataUnit.getChildInfo(this.getCleanDataUnitName());
    const { links } = selfInfo;
    if (!links || links.length === 0)
      return records;
    return records.map(record => {
      return this.buildRecordWithLinkedFields(record, links, parentSelectedRecord);
    });
  }
  buildRecordWithLinkedFields(record, links, parentSelectedRecord) {
    const newRecord = Object.assign({}, record);
    links.forEach(link => {
      const convertedParentSelectedRecord = convertType(this.dataUnit.getField(link.target).dataType, parentSelectedRecord[link.source]);
      newRecord[link.target] = convertedParentSelectedRecord;
    });
    return newRecord;
  }
  handleCleanOnCopy(records) {
    const cleanFields = this.getCleanOnCopyFields();
    if (cleanFields.length > 0) {
      records = records.map(record => this.buildCopyWithCleanedFields(record, cleanFields));
    }
    return records;
  }
  buildCopyWithCleanedFields(record, cleanFields) {
    const newRecord = Object.assign({}, record);
    cleanFields.forEach(fieldName => delete newRecord[fieldName]);
    return newRecord;
  }
  showSuccessMessage(message) {
    ApplicationUtils.info(message, { iconName: "check" });
  }
  isAllowed(flag) {
    return this._permissions ? this._permissions.isSup || this._permissions[flag] : false;
  }
  async buildDataState(actionType) {
    var _a;
    const selectionInfo = this.dataUnit.getSelectionInfo();
    const isStartingInsertionMode = (this.dataUnit.hasDirtyRecords() || this.dataUnit.hasWaitingChanges()) && (selectionInfo === null || selectionInfo === void 0 ? void 0 : selectionInfo.isEmpty());
    const selectedRecord = this.dataUnit.getSelectedRecord();
    let rowMetadata = (_a = this.dataState) === null || _a === void 0 ? void 0 : _a.rowMetadata;
    const allowedOverwrite = [Action.SELECTION_CHANGED, Action.EDITION_CANCELED, Action.NEXT_SELECTED, Action.PREVIOUS_SELECTED];
    if (selectedRecord && (!rowMetadata || allowedOverwrite.includes(actionType))) {
      rowMetadata = await this.doGetRowMetadata(selectedRecord);
    }
    else if (rowMetadata) {
      rowMetadata.getProp = this.buildGetPropRowMetadata(rowMetadata);
    }
    return new DataStateImpl({
      insertionMode: this.dataUnit.hasNewRecord(),
      isStartingInsertionMode,
      hasNext: this.dataUnit.hasNext(),
      hasPrevious: this.dataUnit.hasPrevious(),
      copyMode: this.dataUnit.hasCopiedRecord(),
      isDirty: this.dataUnit.isDirty(),
      hasDirtyRecords: this.dataUnit.hasDirtyRecords(),
      selectedRecords: undefined,
      selectionInfo,
      selectedRecord,
      recordsIsEmpty: this.dataUnit.records.length === 0,
      metadataByRow: this._metadataByRow,
      rowMetadata
    });
  }
  async handleDataSaved(action) {
    var _a, _b, _c;
    const changes = (_a = action === null || action === void 0 ? void 0 : action.payload) === null || _a === void 0 ? void 0 : _a.changes;
    const affectedRecords = (_b = action === null || action === void 0 ? void 0 : action.payload) === null || _b === void 0 ? void 0 : _b.records;
    const newRowMetadata = await this.handleLoadRowMetadata((_c = changes === null || changes === void 0 ? void 0 : changes[0]) === null || _c === void 0 ? void 0 : _c.record);
    if (newRowMetadata) {
      newRowMetadata.getProp = this.buildGetPropRowMetadata(newRowMetadata);
    }
    const recordId = affectedRecords[0].__record__id__;
    this._metadataByRow.set(recordId, newRowMetadata);
    this.dataState = await this.buildDataState();
    let saveOperation = changes[0]._operation.toLowerCase();
    if (saveOperation == 'copy') {
      saveOperation = OperationMap.CLONE;
    }
    if (this.canIgnoreOperationMessage(saveOperation))
      return;
    let msg = this.buildOperationMessage(saveOperation, affectedRecords);
    if (msg != undefined) {
      this.showSuccessMessage(msg);
    }
  }
  canIgnoreOperationMessage(saveOperation) {
    return this.ignoreSaveMessage && !this.isCloneOrInsertOperation(saveOperation);
  }
  isCloneOrInsertOperation(saveOperation) {
    return [OperationMap.CLONE, OperationMap.INSERT].includes(saveOperation);
  }
  buildOperationMessage(saveOperation, records) {
    if (this.isMultiplesUpdate(saveOperation, records)) {
      return this.buildMultipleUpdateMessage(records);
    }
    return this.getMessage('snkDataUnit.saveInfo', records[0], saveOperation);
  }
  buildMultipleUpdateMessage(records, isCancel) {
    /**
     * FIXME: No futuro, precisamos pensar em um mecanismo para deixar as mensagens de feedback mais dinâmicas.
     * Podemo ocorrer cenários onde temos mais de um registro selecionado e também cenários onde a chave primária
     * de um registro se trata de uma chave composta.
     */
    const key = `snkDataUnit.${isCancel ? 'cancelInfo' : 'saveInfo'}`;
    return this.getMessage(key, { records: records }, OperationMap.UPDATE_MULTIPLES);
  }
  isMultiplesUpdate(saveOperation, records) {
    return saveOperation === OperationMap.UPDATE && records.length > 1;
  }
  handleRecordsRemoved(action) {
    var _a, _b;
    let removeFinishMsg = this.buildRemoveFinishMessage(action);
    if (removeFinishMsg != undefined) {
      this.showSuccessMessage(removeFinishMsg);
    }
    const recordsCount = (_b = (_a = this.dataUnit.records) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
    const paginationInfo = this.dataUnit.getPaginationInfo();
    if (paginationInfo) {
      const page = recordsCount > 0 || paginationInfo.hasMore ? paginationInfo.currentPage : 0;
      this.dataUnit.gotoPage(page);
    }
  }
  buildRemoveFinishMessage(action) {
    const records = action.payload.records;
    if (!records || !records.length)
      return undefined;
    if ((records === null || records === void 0 ? void 0 : records.length) > 1)
      return this.getMessage('snkDataUnit.removeAllInfo', { size: records.length });
    return this.getMessage('snkDataUnit.removeInfo', records);
  }
  /**
   * Conforme mecanismo de mensagens, é possível customizar as mensagens dos blocos de construção
   * através de um pequeno modulo na estrutura da aplicação:
   * - Criar um arquivo no seguinte caminho: /messages/appmessages.msg.js.
   * Para conhecer os detalhes do módulo, vide o arquivo neste projeto "/src/lib/message/resources/snk-data-unit.msg.ts"
   */
  getMessage(key, params = undefined, customOperation) {
    if (!params) {
      params = this.getMessageParams();
    }
    if (customOperation) {
      this.messagesBuilder.currentOperation = customOperation;
    }
    return this.messagesBuilder.getMessage(key, params);
  }
  getMessageParams() {
    //FIXME: Devido ao recurso de multiseleção do dataunit, precisaremos criar um mecanismo para
    //oferecer todos os registros selecionados para a mensagem, pois mensagens podem ficar incorretas.
    return this.dataState.selectedRecord;
  }
  getMessageOperation() {
    if (this.dataState.copyMode) {
      return OperationMap.CLONE;
    }
    if (this.dataState.insertionMode || this.dataState.isStartingInsertionMode) {
      return OperationMap.INSERT;
    }
    if (this.dataState.isDirty) {
      return OperationMap.UPDATE;
    }
    return OperationMap.CLEAN;
  }
  async getDataUnitParentOrChild() {
    var _a;
    const cacheName = this.dataUnitName ? this.dataUnitName : this.entityName;
    if (this._parentSnkDataUnit) {
      this._parentDataUnit = await ((_a = this._parentSnkDataUnit) === null || _a === void 0 ? void 0 : _a.getDataUnit());
      return await this._application.getDataUnit(this.entityName, cacheName, this._parentDataUnit, this.configName, this.resourceID);
    }
    else {
      return await this._application.getDataUnit(this.entityName, cacheName, null, this.configName, this.resourceID);
    }
  }
  updateMetadataWithRMP() {
    this.fillFieldsWithRmp(this.dataUnit.metadata);
    this.fillFieldsWithRmPrecision(this.dataUnit.metadata);
    const dataUnitUpdatedMetadata = this.handleDULinksOnMetadataLoaded(this.dataUnit.metadata);
    this.dataUnit.metadata = dataUnitUpdatedMetadata;
  }
  async loadDataUnit() {
    var _a, _b;
    if (this.dataUnit == null && this._application && this.entityName) {
      this.dataUnit = await this.getDataUnitParentOrChild();
    }
    if (this.dataUnit) {
      this.dataUnit.pageSize = this.pageSize;
      this.dataUnit.unsubscribe(this._dataUnitObserver);
      this.dataUnit.subscribe(this._dataUnitObserver);
      if ((_a = this.dataUnit) === null || _a === void 0 ? void 0 : _a.metadata) {
        this.updateMetadataWithRMP();
      }
      this.dataUnit.addInterceptor(this);
      if ((_b = this.dataUnit.records) === null || _b === void 0 ? void 0 : _b.length) {
        const { records } = this.getMetadataByRow(this.dataUnit);
        this.dataUnit.setRecordsKeepingSelection(records);
      }
      this.dataState = await this.buildDataState();
      let resolver;
      while (resolver = this._onDataUnitResolve.pop()) {
        resolver(this.dataUnit);
      }
    }
  }
  getParentSnkDataUnit() {
    let currentElement = this.element;
    while (currentElement.parentNode) {
      if (currentElement.parentNode.nodeName === 'SNK-DATA-UNIT') {
        return currentElement.parentNode;
      }
      currentElement = currentElement.parentNode;
    }
    return;
  }
  handlerLinkFields() {
    var _a, _b, _c, _d;
    const parentDU = (_a = this._parentDataUnit) !== null && _a !== void 0 ? _a : (_b = this.dataUnit) === null || _b === void 0 ? void 0 : _b.getParentDataUnit();
    if (!parentDU)
      return;
    const childName = (_c = this.entityName) !== null && _c !== void 0 ? _c : this.getCleanDataUnitName();
    const child = parentDU.getChildInfo(childName);
    if (!child)
      return;
    const links = (_d = child === null || child === void 0 ? void 0 : child.links) === null || _d === void 0 ? void 0 : _d.map(link => link.target);
    this.handleDUWithLinks(links);
  }
  handleDUWithLinks(fieldsToLink) {
    this.fieldsToLink = fieldsToLink;
    const metadata = Object.assign({}, this.dataUnit.metadata);
    this.processLinks(metadata);
    this.dataUnit.metadata = metadata;
    this.dataUnitFieldsHidded.emit();
  }
  processLinks(metadata) {
    var _a;
    (_a = metadata === null || metadata === void 0 ? void 0 : metadata.fields) === null || _a === void 0 ? void 0 : _a.forEach(field => {
      var _a;
      if ((_a = this.fieldsToLink) === null || _a === void 0 ? void 0 : _a.includes(field.name)) {
        field.visible = false;
      }
    });
  }
  getCleanDataUnitName() {
    var _a;
    const uri = this.dataUnit.name;
    if (StringUtils.isEmpty(uri))
      return uri;
    const matcher = this.REGEX_DATAUNIT_NAME.exec(uri);
    return (_a = matcher === null || matcher === void 0 ? void 0 : matcher[1]) !== null && _a !== void 0 ? _a : uri;
  }
  static getNearestInstance(element) {
    let parent = element.parentElement;
    while (parent) {
      if (parent.tagName.toUpperCase() === "SNK-DATA-UNIT") {
        return parent;
      }
      parent = parent.parentElement;
    }
  }
  instanceMessagesBuilder() {
    var _a;
    if (this.messagesBuilder) {
      return;
    }
    this.messagesBuilder = new SnkMessageBuilder((_a = this.domainMessagesBuilder) !== null && _a !== void 0 ? _a : this.entityName);
  }
  //---------------------------------------------
  // Lifecycle web component
  //---------------------------------------------
  componentWillLoad() {
    this._application = ApplicationContext.getContextValue("__SNK__APPLICATION__");
    this._application.getAllAccess(this.resourceID).then(access => this._permissions = access);
    this._parentSnkDataUnit = this.getParentSnkDataUnit();
    this.instanceMessagesBuilder();
  }
  disconnectedCallback() {
    if (this.dataUnit) {
      this.dataUnit.releaseCallbacks();
    }
  }
  async componentDidLoad() {
    await this.loadDataUnit();
    this.handlerLinkFields();
  }
  render() {
    return (h(Host, null));
  }
  static get is() { return "snk-data-unit"; }
  static get encapsulation() { return "scoped"; }
  static get properties() {
    return {
      "dataState": {
        "type": "unknown",
        "mutable": true,
        "complexType": {
          "original": "DataState",
          "resolved": "DataState",
          "references": {
            "DataState": {
              "location": "local"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{DataState}"
            }],
          "text": "Controla o estado atual dos dados."
        }
      },
      "messagesBuilder": {
        "type": "unknown",
        "mutable": true,
        "complexType": {
          "original": "SnkMessageBuilder",
          "resolved": "SnkMessageBuilder",
          "references": {
            "SnkMessageBuilder": {
              "location": "import",
              "path": "../../lib/message/SnkMessageBuilder"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{SnkMessageBuilder}"
            }],
          "text": "Respons\u00E1vel por flexibilizar e padronizar o uso de mensagens nos blocos de constru\u00E7\u00E3o."
        }
      },
      "dataUnitName": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{string}"
            }],
          "text": "Usado para criar o dataUnit uma \u00FAnica vez.\nSe omitido, ser\u00E1 usado o pr\u00F3prio nome da entidade."
        },
        "attribute": "data-unit-name",
        "reflect": false
      },
      "entityName": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{string}"
            }],
          "text": "Determina qual a entidade que representa os dados em quest\u00E3o."
        },
        "attribute": "entity-name",
        "reflect": false
      },
      "pageSize": {
        "type": "number",
        "mutable": false,
        "complexType": {
          "original": "number",
          "resolved": "number",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{number}"
            }, {
              "name": "default",
              "text": "150"
            }],
          "text": "Determina quantas linhas s\u00E3o retornadas por p\u00E1gina."
        },
        "attribute": "page-size",
        "reflect": false,
        "defaultValue": "150"
      },
      "dataUnit": {
        "type": "unknown",
        "mutable": true,
        "complexType": {
          "original": "DataUnit",
          "resolved": "DataUnit",
          "references": {
            "DataUnit": {
              "location": "import",
              "path": "@sankhyalabs/core"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{DataUnit}"
            }],
          "text": "Uma vez instanciado, pode-se obter o dataUnit por esta propriedade."
        }
      },
      "beforeSave": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "(dataUnit: DataUnit) => boolean | Promise<boolean>",
          "resolved": "(dataUnit: DataUnit) => boolean | Promise<boolean>",
          "references": {
            "DataUnit": {
              "location": "import",
              "path": "@sankhyalabs/core"
            },
            "Promise": {
              "location": "global"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "param",
              "text": "dataUnit - A inst\u00E2ncia do DataUnit."
            }, {
              "name": "returns",
              "text": "`false` para cancelar a a\u00E7\u00E3o, `true` para continuar."
            }],
          "text": "Executado imediatamente antes da a\u00E7\u00E3o de salvar as altera\u00E7\u00F5es. \u00DAtil no caso de valida\u00E7\u00F5es por exemplo.\nCaso retorne \"false\" (ou a promessa se resolva como false), cancela a a\u00E7\u00E3o."
        }
      },
      "afterSave": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "(dataUnit: DataUnit) => void",
          "resolved": "(dataUnit: DataUnit) => void",
          "references": {
            "DataUnit": {
              "location": "import",
              "path": "@sankhyalabs/core"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "param",
              "text": "dataUnit - A inst\u00E2ncia do DataUnit."
            }, {
              "name": "returns",
              "text": undefined
            }],
          "text": "Executado ap\u00F3s a a\u00E7\u00E3o de salvar."
        }
      },
      "useCancelConfirm": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{boolean}"
            }, {
              "name": "default",
              "text": "true"
            }],
          "text": "Determina se ser\u00E1 usado mensagem de confirma\u00E7\u00E3o padr\u00E3o na tentativa de cancelar a edi\u00E7\u00E3o."
        },
        "attribute": "use-cancel-confirm",
        "reflect": false,
        "defaultValue": "true"
      },
      "ignoreSaveMessage": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{boolean}"
            }],
          "text": "Respons\u00E1vel por evitar a mensagem de sucesso ao salvar."
        },
        "attribute": "ignore-save-message",
        "reflect": false
      },
      "configName": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{string}"
            }],
          "text": "Usado para obter configura\u00E7\u00E3o de metadados."
        },
        "attribute": "config-name",
        "reflect": false
      },
      "resourceID": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{string}"
            }],
          "text": "Identificador de recursos como configura\u00E7\u00F5es e acesso."
        },
        "attribute": "resource-i-d",
        "reflect": false
      },
      "domainMessagesBuilder": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{string}"
            }],
          "text": "Define a chave customizada para sobrescrever as mensagens (N\u00E3o pegando pela entidade)"
        },
        "attribute": "domain-messages-builder",
        "reflect": false
      }
    };
  }
  static get states() {
    return {
      "fieldsToLink": {}
    };
  }
  static get events() {
    return [{
        "method": "dataStateChange",
        "name": "dataStateChange",
        "bubbles": false,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [{
              "name": "event",
              "text": "dataStateChange"
            }, {
              "name": "type",
              "text": "{CustomEvent<DataState>}"
            }, {
              "name": "bubbles",
              "text": "false"
            }],
          "text": "Emitido quando h\u00E1 qualquer mudan\u00E7a de estado no DataUnit."
        },
        "complexType": {
          "original": "DataState",
          "resolved": "DataState",
          "references": {
            "DataState": {
              "location": "local"
            }
          }
        }
      }, {
        "method": "dataUnitReady",
        "name": "dataUnitReady",
        "bubbles": false,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [{
              "name": "event",
              "text": "dataUnitReady"
            }, {
              "name": "type",
              "text": "{CustomEvent<DataUnit>}"
            }, {
              "name": "bubbles",
              "text": "false"
            }],
          "text": "Emitido quando o DataUnit est\u00E1 pronto."
        },
        "complexType": {
          "original": "DataUnit",
          "resolved": "DataUnit",
          "references": {
            "DataUnit": {
              "location": "import",
              "path": "@sankhyalabs/core"
            }
          }
        }
      }, {
        "method": "messagesBuilderUpdated",
        "name": "messagesBuilderUpdated",
        "bubbles": false,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [{
              "name": "event",
              "text": "messagesBuilderUpdated"
            }, {
              "name": "type",
              "text": "{CustomEvent<SnkMessageBuilder>}"
            }, {
              "name": "bubbles",
              "text": "false"
            }],
          "text": "Emitido quando o messagesBuilder \u00E9 atualizado."
        },
        "complexType": {
          "original": "SnkMessageBuilder",
          "resolved": "SnkMessageBuilder",
          "references": {
            "SnkMessageBuilder": {
              "location": "import",
              "path": "../../lib/message/SnkMessageBuilder"
            }
          }
        }
      }, {
        "method": "dataUnitFieldsHidded",
        "name": "dataUnitFieldsHidded",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [{
              "name": "event",
              "text": "dataUnitFieldsHidded"
            }, {
              "name": "type",
              "text": "{CustomEvent<void>}"
            }],
          "text": "Emitido quando h\u00E1 campos no DataUnit que devem ser ocultados."
        },
        "complexType": {
          "original": "void",
          "resolved": "void",
          "references": {}
        }
      }, {
        "method": "insertionMode",
        "name": "insertionMode",
        "bubbles": false,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [{
              "name": "event",
              "text": "insertionMode"
            }, {
              "name": "type",
              "text": "{CustomEvent<void>}"
            }, {
              "name": "bubbles",
              "text": "false"
            }],
          "text": "Emitido quando um registro \u00E9 adicionado ou copiado."
        },
        "complexType": {
          "original": "void",
          "resolved": "void",
          "references": {}
        }
      }, {
        "method": "cancelEdition",
        "name": "cancelEdition",
        "bubbles": false,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [{
              "name": "event",
              "text": "cancelEdition"
            }, {
              "name": "type",
              "text": "{CustomEvent<void>}"
            }, {
              "name": "bubbles",
              "text": "false"
            }],
          "text": "Emitido quando se cancela uma inser\u00E7\u00E3o ou c\u00F3pia."
        },
        "complexType": {
          "original": "void",
          "resolved": "void",
          "references": {}
        }
      }];
  }
  static get methods() {
    return {
      "getDataUnit": {
        "complexType": {
          "signature": "() => Promise<DataUnit>",
          "parameters": [],
          "references": {
            "Promise": {
              "location": "global"
            },
            "DataUnit": {
              "location": "import",
              "path": "@sankhyalabs/core"
            }
          },
          "return": "Promise<DataUnit>"
        },
        "docs": {
          "text": "Obt\u00E9m o dataUnit.",
          "tags": [{
              "name": "returns",
              "text": "Uma promessa que resolve com a inst\u00E2ncia do DataUnit."
            }, {
              "name": "async",
              "text": undefined
            }]
        }
      },
      "getSelectedRecordsIDsInfo": {
        "complexType": {
          "signature": "() => Promise<Array<IRecordID>>",
          "parameters": [],
          "references": {
            "Promise": {
              "location": "global"
            },
            "IRecordID": {
              "location": "import",
              "path": "../snk-data-exporter/interfaces/IRecordID"
            },
            "Array": {
              "location": "global"
            }
          },
          "return": "Promise<IRecordID[]>"
        },
        "docs": {
          "text": "M\u00E9todo que retorna a lista de IDs dos registros selecionados.",
          "tags": [{
              "name": "returns",
              "text": "Retorna uma promessa que resolve com a lista de IDs dos registros selecionados."
            }, {
              "name": "async",
              "text": undefined
            }]
        }
      },
      "getFieldsWithRmp": {
        "complexType": {
          "signature": "() => Promise<string[]>",
          "parameters": [],
          "references": {
            "Promise": {
              "location": "global"
            }
          },
          "return": "Promise<string[]>"
        },
        "docs": {
          "text": "Retorna os campos que possuem a propriedade \"rmp\" (Row Metadata Provider).",
          "tags": [{
              "name": "returns",
              "text": "Uma promessa que resolve com um array de nomes de campos."
            }, {
              "name": "async",
              "text": undefined
            }]
        }
      },
      "getFieldsWithRmPrecision": {
        "complexType": {
          "signature": "() => Promise<string[]>",
          "parameters": [],
          "references": {
            "Promise": {
              "location": "global"
            }
          },
          "return": "Promise<string[]>"
        },
        "docs": {
          "text": "Retorna os campos que possuem a propriedade \"rm_precision\" (Row Metadata Precision).",
          "tags": [{
              "name": "returns",
              "text": "Uma promessa que resolve com um array de nomes de campos."
            }, {
              "name": "async",
              "text": undefined
            }]
        }
      },
      "getRowMetadata": {
        "complexType": {
          "signature": "(record?: Record | string) => Promise<RowMetadata>",
          "parameters": [{
              "tags": [{
                  "name": "param",
                  "text": "record - O registro ou ID do registro para o qual buscar os metadados. Se omitido, usa o registro selecionado."
                }],
              "text": "- O registro ou ID do registro para o qual buscar os metadados. Se omitido, usa o registro selecionado."
            }],
          "references": {
            "Promise": {
              "location": "global"
            },
            "RowMetadata": {
              "location": "local"
            },
            "Record": {
              "location": "import",
              "path": "@sankhyalabs/core"
            }
          },
          "return": "Promise<RowMetadata>"
        },
        "docs": {
          "text": "Busca os metadados da linha selecionada.",
          "tags": [{
              "name": "param",
              "text": "record - O registro ou ID do registro para o qual buscar os metadados. Se omitido, usa o registro selecionado."
            }, {
              "name": "returns",
              "text": "Uma promessa que resolve com os metadados da linha."
            }, {
              "name": "async",
              "text": undefined
            }]
        }
      }
    };
  }
  static get elementRef() { return "element"; }
  static get watchers() {
    return [{
        "propName": "pageSize",
        "methodName": "observePageSize"
      }, {
        "propName": "dataUnitName",
        "methodName": "observeDataUnitName"
      }, {
        "propName": "entityName",
        "methodName": "observeEntityName"
      }, {
        "propName": "dataState",
        "methodName": "observeDataState"
      }, {
        "propName": "dataUnit",
        "methodName": "observeDataUnit"
      }, {
        "propName": "messagesBuilder",
        "methodName": "observeMessagesBuilder"
      }];
  }
  static get listeners() {
    return [{
        "name": "snkMasterFormConfigChange",
        "method": "onMasterFormConfigChange",
        "target": undefined,
        "capture": false,
        "passive": false
      }];
  }
}
class DataStateImpl {
  constructor(datastate) {
    this.copyMode = datastate.copyMode;
    this.insertionMode = datastate.insertionMode;
    this.isStartingInsertionMode = datastate.isStartingInsertionMode;
    this.isDirty = datastate.isDirty;
    this.hasDirtyRecords = datastate.hasDirtyRecords;
    this.hasNext = datastate.hasNext;
    this.hasPrevious = datastate.hasPrevious;
    this.selectionInfo = datastate.selectionInfo;
    this.selectedRecord = datastate.selectedRecord;
    this.recordsIsEmpty = datastate.recordsIsEmpty;
    this.metadataByRow = datastate.metadataByRow;
    this.rowMetadata = datastate.rowMetadata;
  }
}
