import { Action, ObjectUtils, UserInterface } from '@sankhyalabs/core';
import { EZ_GRID_LOADING_SOURCE, PRESENTATION_COL_ID_PROP_NAME, PRESENTATION_FROM_COL_PROP_NAME } from "../../../../utils/constants";
import { DataSourceInterceptor } from "./DataSourceInterceptor";
export default class DataSource {
  handleDataSaved(action) {
    if (this._options) {
      this._options.onPaginationUpdate(this._dataUnit.getPaginationInfo());
    }
    this._controller.clearInvalidCells();
    this.handleRefreshOrReload(action);
    this._controller.processContinuousInsert();
    this._controller.showSelectionColumn();
  }
  handleEditionCanceled(action) {
    if (this._options.enableGridInsert) {
      this._controller.processEditionCanceled();
    }
    this.handleRefreshOrReload(action);
    this._controller.showSelectionColumn();
  }
  handleSavingCanceled(action) {
    var _a, _b, _c, _d;
    if (!((_a = action.payload) === null || _a === void 0 ? void 0 : _a.fields) || !((_b = action.payload) === null || _b === void 0 ? void 0 : _b.recordId))
      return;
    this._controller.savingCanceled((_c = action.payload) === null || _c === void 0 ? void 0 : _c.fields, (_d = action.payload) === null || _d === void 0 ? void 0 : _d.recordId);
  }
  handleDataChanged(action) {
    if (!this._options.enableGridInsert) {
      this.handleRefresh(action);
      return;
    }
    this.updateGridRowNodes(this.createRecordIdList(action));
  }
  createRecordIdList(action) {
    var _a, _b;
    if ((_a = action.payload) === null || _a === void 0 ? void 0 : _a.records)
      return (_b = action.payload) === null || _b === void 0 ? void 0 : _b.records;
    return this._dataUnit.getSelectedRecord() ? [this._dataUnit.getSelectedRecord().__record__id__] : [];
  }
  /**
   *  Nesse ponto, o registro já se encontra atualizado no DU,
   *  basta então passar seu valor para que a garde possa atualizar sua linha.
  */
  updateGridRowNodes(recordIDList) {
    const recordsToUpdate = recordIDList === null || recordIDList === void 0 ? void 0 : recordIDList.map(id => this.getRecordById(id));
    this._controller.updateRows(recordsToUpdate);
  }
  getRecordById(recordId) {
    return this._dataUnit.records.find(record => record['__record__id__'] === recordId);
  }
  /**
   * Em caso de necessidade de reload (isWaitingToReload), eh preciso chamar o dataUnit.gotoPage, pois ele aplica tambem a ordenacao dos registros,
   * enquanto que o _controller.refresh apenas recarrega os dados na ordem que atua.
   */
  handleRefreshOrReload(action) {
    if (this._dataUnit.isWaitingToReload()) {
      this.handleReload();
      return;
    }
    this.handleRefresh(action);
  }
  handleReload() {
    this._dataUnit.setWaitingToReload(false);
    this._dataUnit.gotoPage(0);
  }
  handleRefresh(action) {
    if (this.isSilentChange(action)) {
      return;
    }
    this._controller.refresh(action.type === Action.DATA_SAVED);
  }
  handleRecordsAdded() {
    if (this._options.enableGridInsert) {
      this._controller.hideSelectionColumn();
      this._controller.refresh();
      this.focusOnNewRecord();
    }
  }
  focusOnNewRecord() {
    /**
     * O SetTimeout eh utilizado pois é preciso aguardar que o novo registro seja renderizado na grade.
     * Tentamos utilizar a api da grade para adicionar um event listener, porém na versão que utilizamos, não existe
     * algo que resolva nosso cenário.
     *
     * Sugiro no futuro utilizar algo como o applyServerSideTransaction, porém será preciso lidar com os handlers
     * e callbacks implementados no AgGridController e GridEditionManager.
     */
    setTimeout(() => {
      this._controller.startEditionOnLastRow();
    }, 1000);
  }
  updateLoadedRecords(action) {
    const records = action.payload;
    if ((records === null || records === void 0 ? void 0 : records.length) > 0) {
      records.forEach(record => {
        this._controller.changeValues(record, [record.__record__id__]);
      });
    }
  }
  handleFocusFirstRow() {
    if (this._options.autoFocus) {
      this._controller.setFocusFirstRow();
    }
  }
  isSilentChange(action) {
    //TODO: Provisorio p/ não atualizar a grade quando a alteração for apenas de campos transisntes
    var _a, _b;
    if (!(action === null || action === void 0 ? void 0 : action.payload)) {
      return false;
    }
    if (action.type === Action.DATA_CHANGED) {
      return (_a = Object.keys(action === null || action === void 0 ? void 0 : action.payload)) === null || _a === void 0 ? void 0 : _a.some(key => { var _a; return (key.startsWith("transient.") && action.payload[key]) || ((_a = action.payload.records) === null || _a === void 0 ? void 0 : _a.length) > 0; });
    }
    if (action.type === Action.EDITION_CANCELED) {
      return ((_b = action === null || action === void 0 ? void 0 : action.payload) === null || _b === void 0 ? void 0 : _b.silent) === true;
    }
  }
  constructor(dataUnit, controller, options, resolveLoading) {
    var _a;
    this.RECORD_ARCHIVE = "__RECORD_ARCHIVE__";
    this.duObserver = (action) => {
      var _a;
      switch (action.type) {
        case Action.METADATA_LOADED:
          this._controller.setColumnsDef(this.buildColumnDefs());
          (_a = this._resolveLoading) === null || _a === void 0 ? void 0 : _a.call(this);
          break;
        case Action.LOADING_DATA:
          this._waitingForLoad = true;
          this._controller.refresh();
          break;
        case Action.DATA_LOADED:
          this._waitingForLoad = false;
          this.callbackGetRows(this._lastLoadingParams);
          if (this._options) {
            this._options.onPaginationChange(this._dataUnit.getPaginationInfo(), action.payload.selectFirstRecord);
          }
          this.updateSelection();
          this.handleFocusFirstRow();
          break;
        case Action.PAGINATION_UPDATED:
          if (this._options) {
            this._options.onPaginationUpdate(this._dataUnit.getPaginationInfo());
          }
          break;
        case Action.RECORDS_ADDED:
        case Action.RECORDS_COPIED:
          this.handleRecordsAdded();
          break;
        case Action.DATA_SAVED:
          this.handleDataSaved(action);
          break;
        case Action.EDITION_CANCELED:
          this.handleEditionCanceled(action);
          break;
        case Action.DATA_CHANGED:
        case Action.DATA_RESOLVED:
          this.handleDataChanged(action);
          break;
        case Action.RECORDS_REMOVED:
          this.handleRefresh(action);
          break;
        case Action.SELECTION_CHANGED:
        case Action.NEXT_SELECTED:
        case Action.PREVIOUS_SELECTED:
          this.updateSelection();
          break;
        case Action.RECORD_LOADED:
          this.updateLoadedRecords(action);
          break;
        case Action.SAVING_CANCELED:
          this.handleSavingCanceled(action);
          break;
      }
    };
    try {
      this._resolveLoading = resolveLoading;
      this._dataUnit = dataUnit;
      this._controller = controller;
      this._controller.setColumnsDef(this.buildColumnDefs());
      this._options = options;
      this._dataUnit.subscribe(this.duObserver);
      this._dataUnit.addInterceptor(new DataSourceInterceptor(this._controller));
    }
    finally {
      if (this._dataUnit.metadata)
        (_a = this._resolveLoading) === null || _a === void 0 ? void 0 : _a.call(this);
    }
  }
  updateSelection() {
    const selectionInfo = this._dataUnit.getSelectionInfo();
    if (selectionInfo.isAllRecords()) {
      this._controller.selectAll(true);
    }
    else {
      this._controller.selectRows(selectionInfo.recordIds, true);
    }
  }
  setAutoFocus(autoFocus) {
    this._options.autoFocus = autoFocus;
  }
  setEnableGridInsert(enable) {
    this._options.enableGridInsert = enable;
  }
  getRows(params) {
    if (this.needReload(params)) {
      this._lastLoadingParams = params;
      this._dataUnit.loadData(this.quickFilter, undefined, true, EZ_GRID_LOADING_SOURCE).then(() => this.updateSelection());
    }
    else {
      this._lastLoadingParams = params;
      if (!this._waitingForLoad) {
        this.callbackGetRows(params);
        this.updateSelection();
      }
    }
  }
  needReload(currentLoading) {
    if (this._lastLoadingParams) {
      return ObjectUtils.objectToString(currentLoading.request) != ObjectUtils.objectToString(this._lastLoadingParams.request);
    }
    return false;
  }
  callbackGetRows(params) {
    if (params) {
      params.success({ rowData: this._dataUnit.records });
    }
  }
  destroy() {
    this._dataUnit.unsubscribe(this.duObserver);
  }
  registryListeners() {
    var _a, _b;
    (_a = this._dataUnit) === null || _a === void 0 ? void 0 : _a.unsubscribe(this.duObserver);
    (_b = this._dataUnit) === null || _b === void 0 ? void 0 : _b.subscribe(this.duObserver);
  }
  getDescriptionField(descriptionName, entityName) {
    const descriptionFieldName = `${entityName}.${descriptionName}`;
    return this._dataUnit.metadata.fields.find((field) => field.name === descriptionFieldName);
  }
  buildColumnDefs() {
    try {
      const gridConfig = this._controller.getGridConfig() || [];
      const columnDefs = [];
      if (this._dataUnit.metadata) {
        this._dataUnit.metadata.fields.sort((a, b) => {
          const indexA = gridConfig.findIndex(value => value.name === a.name);
          const indexB = gridConfig.findIndex(value => value.name === b.name);
          return indexA - indexB;
        });
        this._dataUnit.metadata.fields.forEach(f => {
          var _a, _b, _c, _d, _e, _f;
          if (f.visible !== false && f.name !== this.RECORD_ARCHIVE) {
            const props = new Map();
            const properties = this._dataUnit.getField(f.name).properties;
            for (const value in properties) {
              props.set(value, properties[value]);
            }
            const col = { label: f.label, name: f.name, userInterface: f.userInterface, options: (_a = this._dataUnit.getField(f.name).properties) === null || _a === void 0 ? void 0 : _a.options, props };
            columnDefs.push(col);
            if (f.userInterface === UserInterface.SEARCHPLUS && ((_b = f.properties) === null || _b === void 0 ? void 0 : _b.DESCRIPTIONFIELD)) {
              const descriptionField = this.getDescriptionField((_c = f.properties) === null || _c === void 0 ? void 0 : _c.DESCRIPTIONFIELD, (_d = f.properties) === null || _d === void 0 ? void 0 : _d.ENTITYNAME);
              if (descriptionField) {
                descriptionField.label = `${(_e = f.properties) === null || _e === void 0 ? void 0 : _e.DESCRIPTIONFIELDLABEL} (${(_f = f.properties) === null || _f === void 0 ? void 0 : _f.DESCRIPTIONENTITY})`;
              }
              else {
                const descriptionCol = this.buildDescriptionColumn(f);
                columnDefs.push(descriptionCol);
                f.properties[PRESENTATION_COL_ID_PROP_NAME] = descriptionCol.name;
              }
            }
          }
        });
      }
      return columnDefs;
    }
    catch (error) {
      console.warn(error);
    }
  }
  buildDescriptionColumn(field) {
    var _a, _b, _c, _d;
    const props = new Map();
    props.set(PRESENTATION_FROM_COL_PROP_NAME, field.name);
    const descriptionFieldName = `${(_a = field.properties) === null || _a === void 0 ? void 0 : _a.ENTITYNAME}.${(_b = field.properties) === null || _b === void 0 ? void 0 : _b.DESCRIPTIONFIELD}`;
    this._dataUnit.addSourceFieldValue(descriptionFieldName, field.name);
    return {
      label: `${(_c = field.properties) === null || _c === void 0 ? void 0 : _c.DESCRIPTIONFIELDLABEL} (${(_d = field.properties) === null || _d === void 0 ? void 0 : _d.DESCRIPTIONENTITY})`,
      name: descriptionFieldName,
      userInterface: UserInterface.SHORTTEXT,
      props
    };
  }
}
