import { LockManager, LockManagerOperation, StringUtils, UserInterface } from '@sankhyalabs/core';
import { KeyCode, } from "@ag-grid-community/core";
import { RecordValidationProcessor } from '../../../../utils/validators/recordvalidator/RecordValidationProcessor';
import EzCellEditor from './editor/EzCellEditor';
import EzGridCustomCellEditor from './components/EzGridCustomCellEditor';
import EzGridCustomCellRender from './components/EzGridCustomCellRender';
import { DataBinder } from '../../../../utils';
import { PRESENTATION_COL_ID_PROP_NAME } from '../../../../utils/constants';
export default class GridEditionManager {
  constructor(dataUnit, useEnterLikeTab, recordsValidator, editionIsDisabled, customEditors, customRenders, enableContinuousInsert, enableGridInsert, lockerId) {
    this._inProgressChanges = [];
    this._dataUnit = dataUnit;
    this._recordValidationProcessor = new RecordValidationProcessor(this._dataUnit, {
      getRequiredFields: () => this.getRequiredFields(),
      markAsInvalid: (invalidField, recordId) => { this.pushInvalidCell(invalidField, recordId); },
      getMessageForField: () => null,
    }, recordsValidator);
    this._useEnterLikeTab = useEnterLikeTab;
    this._editionIsDisabled = editionIsDisabled;
    this._enableContinuousInsert = enableContinuousInsert;
    this._enableGridInsert = enableGridInsert;
    this._customEditors = customEditors;
    this._customRenders = customRenders;
    this._lockerId = lockerId;
  }
  configureGrid(options) {
    this._gridOptions = options;
    options.readOnlyEdit = true;
    options.onCellEditRequest = evt => this.onCellEditRequest(evt);
    options.onCellEditingStarted = async (evt) => {
      var _a;
      if (this._dataUnit.hasNewRecord()) {
        this.handleCellEditingStartedRowAdd(evt);
      }
      else {
        this._lastCellOpened = this._lastCellEdited = { rowIndex: evt.rowIndex, column: evt.column, rowPinned: undefined };
      }
      (_a = this._currentEditLock) === null || _a === void 0 ? void 0 : _a.call(this);
      this._currentEditLock = LockManager.lock(this._lockerId, LockManagerOperation.TASKBAR_CLICK);
    };
    options.onCellEditingStopped = () => {
      var _a;
      (_a = this._currentEditLock) === null || _a === void 0 ? void 0 : _a.call(this);
    };
    options.onCellKeyDown = evt => {
      if (evt.event && evt["column"]) {
        this.onCellKeyDown(evt);
      }
    };
    options.isRowSelectable = (node) => this.isSelectableRow(node);
    options.onCellFocused = () => this.lockFocusWhenAddRow();
    return options;
  }
  updateCurrentEditorGuiValue(updatedRowValue) {
    const editorInstances = this._gridOptions.api.getCellEditorInstances();
    if (!editorInstances || !editorInstances.length)
      return;
    const currentEditor = editorInstances[0];
    const fieldMetadata = currentEditor.getFieldMetadata();
    const fieldName = fieldMetadata.name;
    const value = updatedRowValue[fieldName];
    currentEditor.setGuiValue(value);
  }
  lockFocusWhenAddRow() {
    var _a, _b, _c, _d;
    const api = this._gridOptions.api;
    const newFocusCell = api.getFocusedCell();
    const rowIndex = newFocusCell === null || newFocusCell === void 0 ? void 0 : newFocusCell.rowIndex;
    const recordId = (_b = (_a = this._dataUnit.getAddedRecords()) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.__record__id__;
    const rowNode = api.getRowNode(recordId);
    if (!this._dataUnit.hasNewRecord() || rowIndex == (rowNode === null || rowNode === void 0 ? void 0 : rowNode.rowIndex)) {
      return;
    }
    const lastCell = (_d = (_c = this._lastCellOpened) !== null && _c !== void 0 ? _c : this._lastCellEdited) !== null && _d !== void 0 ? _d : this._lastCellClicked;
    if ((lastCell === null || lastCell === void 0 ? void 0 : lastCell.rowIndex) < (rowNode === null || rowNode === void 0 ? void 0 : rowNode.rowIndex)) {
      return;
    }
    api.clearFocusedCell();
    if (!lastCell || (rowNode === null || rowNode === void 0 ? void 0 : rowNode.rowIndex) == undefined) {
      return;
    }
    lastCell.rowIndex = rowNode === null || rowNode === void 0 ? void 0 : rowNode.rowIndex;
    this.focusOnCell(lastCell);
  }
  handleCellEditingStartedRowAdd(evt) {
    var _a, _b, _c, _d, _e;
    const nodeId = (_a = evt.node) === null || _a === void 0 ? void 0 : _a.id;
    const addedRecordId = (_c = (_b = this._dataUnit.getAddedRecords()) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.__record__id__;
    if (nodeId == addedRecordId) {
      this._lastCellOpened = this._lastCellEdited = { rowIndex: evt.rowIndex, column: evt === null || evt === void 0 ? void 0 : evt.column, rowPinned: undefined };
      return;
    }
    evt.api.stopEditing();
    const rowIndex = (_d = evt.api.getRowNode(addedRecordId)) === null || _d === void 0 ? void 0 : _d.rowIndex;
    this._lastCellOpened = this._lastCellEdited = { rowIndex: rowIndex, column: (_e = this._lastCellEdited) === null || _e === void 0 ? void 0 : _e.column, rowPinned: undefined };
  }
  isSelectableRow(node) {
    if (!this._dataUnit.hasNewRecord()) {
      return true;
    }
    if (!this._dataUnit.isNewRecord(node.id)) {
      return false;
    }
    return true;
  }
  canContinuousInsert() {
    var _a;
    return this._enableContinuousInsert && (this._dataUnit.records.length - 1) === ((_a = this._lastCellEdited) === null || _a === void 0 ? void 0 : _a.rowIndex);
  }
  async proceedAutoSave() {
    var _a;
    //Aguarda outras promises entrarem na fila
    await new Promise(resolve => setTimeout(resolve, 100));
    if (this._inProgressChanges.length) {
      await Promise.all((_a = this._inProgressChanges) === null || _a === void 0 ? void 0 : _a.map(({ promise }) => promise));
    }
    if (!this._dataUnit.isDirty()) {
      this.saveSuccess();
      return true;
    }
    const currentRecord = this._dataUnit.getSelectedRecord();
    if (currentRecord == undefined) {
      return true;
    }
    this.clearInvalidCells(false);
    try {
      await this._recordValidationProcessor.validate(true);
      await this.saveDataUnit();
      this.saveSuccess();
      return true;
    }
    catch (reason) {
      this.saveFail(reason);
      return false;
    }
  }
  async saveDataUnit() {
    await this._dataUnit.saveData();
    return true;
  }
  async recordValidate() {
    if (!this._dataUnit.isDirty())
      return;
    try {
      this.clearInvalidCells(false);
      await this._recordValidationProcessor.validate(false);
    }
    catch (error) { }
    this._gridOptions.api.refreshCells({ force: true });
  }
  navigateByEnterKey(keyboardEvent) {
    const backwards = keyboardEvent.shiftKey;
    if (this._useEnterLikeTab) {
      if (backwards) {
        this._gridOptions.api.tabToPreviousCell(keyboardEvent);
      }
      else {
        this._gridOptions.api.tabToNextCell(keyboardEvent);
      }
    }
    else {
      const editionCell = this._gridOptions.api.getEditingCells()[0];
      if (editionCell) {
        let { rowIndex } = editionCell;
        this.nextEditionRow(rowIndex, editionCell.column, backwards);
      }
    }
  }
  getComponents() {
    return { ezCellEditor: EzCellEditor };
  }
  verifyClickToEdition(api, cellClicked) {
    var _a, _b;
    const selectedRecordID = (_a = this._dataUnit.getSelectedRecord()) === null || _a === void 0 ? void 0 : _a.__record__id__;
    if (this._dataUnit.hasNewRecord() && selectedRecordID) {
      const dataUnitRecordIndex = (_b = api.getRowNode(selectedRecordID)) === null || _b === void 0 ? void 0 : _b.rowIndex;
      if (dataUnitRecordIndex != cellClicked.rowIndex)
        return;
    }
    if (this.isSecondClick(cellClicked)) {
      api.startEditingCell({ rowIndex: cellClicked.rowIndex, colKey: cellClicked.column.getColId() });
    }
    this._lastCellClicked = cellClicked;
  }
  configureColumn(col) {
    col.cellEditor = 'ezCellEditor';
    if (col.editable != false) {
      col.editable = params => this.canEdit(params);
    }
    col.suppressKeyboardEvent = (params) => {
      if (params.event.key === KeyCode.ESCAPE && params.editing) {
        return this._dataUnit.isDirty();
      }
      return params.event.key === KeyCode.ENTER;
    };
    col.cellEditorSelector = (params) => {
      var _a;
      if ((_a = this._customEditors) === null || _a === void 0 ? void 0 : _a.has(params.colDef.field)) {
        const customEditor = this._customEditors.get(params.colDef.field);
        return {
          component: EzGridCustomCellEditor,
          params: {
            customEditorsInfo: customEditor,
          }
        };
      }
    };
    col.cellRendererSelector = (params) => {
      var _a;
      if ((_a = this._customRenders) === null || _a === void 0 ? void 0 : _a.has(params.colDef.field)) {
        const customRender = this._customRenders.get(params.colDef.field);
        return {
          component: EzGridCustomCellRender,
          params: {
            customRendersInfo: customRender,
          }
        };
      }
    };
    return col;
  }
  getRequiredFields() {
    const formRequiredFields = this.getFormRequiredFields();
    return this._gridOptions.columnApi.getAllDisplayedColumns()
      .filter(column => {
      const fieldDefinition = this._dataUnit.getField(column.getColId());
      return (fieldDefinition && fieldDefinition.required) || formRequiredFields.includes(column.getColId());
    })
      .map(column => column.getColId());
  }
  getFormRequiredFields() {
    const formRequiredFields = [];
    const dataBinders = DataBinder.getDataBindersByDataUnit(this._dataUnit);
    dataBinders.forEach(dataBinder => {
      formRequiredFields.push(...dataBinder.getFormRequiredFields());
    });
    return formRequiredFields;
  }
  saveSuccess() {
    var _a;
    this.focusOnCell(this._targetEditionCell);
    if (((_a = this._targetEditionCell) === null || _a === void 0 ? void 0 : _a.rowIndex) !== undefined) {
      this._dataUnit.setSelectionByIndex([this._targetEditionCell.rowIndex]);
    }
    this._lastCellEdited = undefined;
    this._targetEditionCell = undefined;
    this._isGridEdition = false;
  }
  saveFail(reason) {
    var _a;
    this.focusOnCell((_a = this._lastCellOpened) !== null && _a !== void 0 ? _a : this._lastCellEdited);
    this._isGridEdition = false;
    if (reason) {
      throw reason;
    }
  }
  focusOnCell(cell) {
    var _a;
    if (cell == undefined) {
      return;
    }
    const { rowIndex, column } = cell;
    (_a = this._gridOptions.api.getDisplayedRowAtIndex(rowIndex)) === null || _a === void 0 ? void 0 : _a.setSelected(true, true);
    this._gridOptions.api.clearRangeSelection();
    this._gridOptions.api.addCellRange({ rowStartIndex: rowIndex, rowEndIndex: rowIndex, columns: [column] });
    this._gridOptions.api.startEditingCell({ colKey: column.getColId(), rowIndex });
    this._gridOptions.api.setFocusedCell(rowIndex, column.getColId());
    const cellEditor = this._gridOptions.api.getCellEditorInstances()[0];
    if (cellEditor) {
      cellEditor.focusIn();
    }
  }
  onCellKeyDown(event) {
    const keyboardEvent = event.event;
    switch (keyboardEvent.key) {
      case KeyCode.ENTER:
        this.processEnterKey(keyboardEvent, event);
        break;
      case KeyCode.UP:
        this.editPreviousRow(event.api);
        break;
      case KeyCode.DOWN:
        this.editNextRow(event.api);
        break;
    }
  }
  processEnterKey(keyboardEvent, event) {
    const editionCell = event.api.getEditingCells()[0];
    if (!editionCell) {
      event.api.startEditingCell({ colKey: event.column.getColId(), rowIndex: event.rowIndex });
      return;
    }
    this.navigateByEnterKey(keyboardEvent);
  }
  nextEditionRow(rowIndex, column, backwards) {
    if (backwards) {
      rowIndex = rowIndex - 1;
      if (rowIndex < 0) {
        rowIndex = this._dataUnit.records.length - 1;
      }
    }
    else {
      const actualRowIndex = rowIndex;
      rowIndex = rowIndex + 1;
      if (rowIndex >= this._dataUnit.records.length) {
        rowIndex = !this._enableGridInsert ? 0 : actualRowIndex;
      }
    }
    this._targetEditionCell = new TargetEdition(rowIndex, column, true);
    this.finishEdition();
  }
  editPreviousRow(api) {
    this.moveEditionVertically(api, -1);
  }
  editNextRow(api) {
    this.moveEditionVertically(api, 1);
  }
  moveEditionVertically(api, offset) {
    if (this._dataUnit.hasNewRecord() && this._isGridEdition) {
      return;
    }
    const editionCell = api.getEditingCells()[0];
    if (!editionCell) {
      return;
    }
    const rowIndex = editionCell.rowIndex + offset;
    if (api.getDisplayedRowAtIndex(rowIndex)) {
      this._targetEditionCell = new TargetEdition(rowIndex, editionCell.column, offset < 0);
      this.finishEdition();
    }
  }
  finishEdition() {
    this._gridOptions.api.stopEditing();
    setTimeout(() => {
      //O Timeout foi inserido para permitir que o ciclo de finalização prossiga
      //pois precisamos aguardar o fim da edição para executar o autosave.
      this.proceedAutoSave();
    }, 0);
  }
  canEdit(params) {
    if (this._editionIsDisabled()) {
      return false;
    }
    return this.isColEditable(params.colDef.field);
  }
  isColEditable(fieldName) {
    const fieldDefinition = this._dataUnit.getField(fieldName);
    if (fieldDefinition && !this.isTypeFile(fieldDefinition)) {
      return !fieldDefinition.readOnly;
    }
    return false;
  }
  isTypeFile(file) {
    return file.userInterface === UserInterface.FILE;
  }
  isSecondClick(cellClicked) {
    if (!this._lastCellClicked) {
      return false;
    }
    if (this._lastCellClicked.column != cellClicked.column) {
      return false;
    }
    if (this._lastCellClicked.rowIndex != cellClicked.rowIndex) {
      return false;
    }
    return true;
  }
  onCellEditRequest(event) {
    var _a;
    const value = event.newValue;
    if (event.oldValue == value) {
      (_a = this._currentEditLock) === null || _a === void 0 ? void 0 : _a.call(this);
      return;
    }
    if (value instanceof Promise) {
      const progressChange = {
        promise: undefined,
        resolve: () => { }
      };
      progressChange.promise = new Promise((resolve) => {
        progressChange.resolve = resolve;
      });
      this._inProgressChanges.push(progressChange);
      this.handlePromiseValue(event, value);
      return;
    }
    this.updateCellValue(event, value);
  }
  async handlePromiseValue(event, value) {
    const fieldName = event.colDef.field;
    const fieldDescriptor = this._dataUnit.getField(event.colDef.colId);
    const initialValue = (fieldDescriptor === null || fieldDescriptor === void 0 ? void 0 : fieldDescriptor.userInterface) === UserInterface.SEARCH ? value : event.oldValue;
    event.node.setData(Object.assign(Object.assign({}, event.data), { [fieldName]: initialValue }));
    const resolved = await value;
    return this.updateCellValue(event, resolved);
  }
  async updateCellValue(event, newValue) {
    var _a, _b, _c;
    const fieldName = event.colDef.field;
    const fieldDescriptor = this._dataUnit.getField(fieldName);
    if (newValue == '' && newValue != this._dataUnit.getFieldValue(fieldName) && (fieldDescriptor === null || fieldDescriptor === void 0 ? void 0 : fieldDescriptor.userInterface) === UserInterface.SEARCH) {
      newValue = this._dataUnit.getFieldValue(fieldName);
    }
    const newData = { [fieldName]: newValue };
    const descriptionColName = (_a = fieldDescriptor === null || fieldDescriptor === void 0 ? void 0 : fieldDescriptor.properties) === null || _a === void 0 ? void 0 : _a[PRESENTATION_COL_ID_PROP_NAME];
    if (!StringUtils.isEmpty(descriptionColName)) {
      newData[descriptionColName] = newValue === null || newValue === void 0 ? void 0 : newValue.label;
    }
    event.node.setData(Object.assign(Object.assign({}, event.data), { [fieldName]: newValue }));
    (_b = this._currentEditLock) === null || _b === void 0 ? void 0 : _b.call(this);
    if (event.oldValue !== newValue) {
      this._isGridEdition = true;
      await this._dataUnit.setFieldValue(fieldName, newValue, [event.data.__record__id__], { suppressCreateNewRecord: true });
      this._lastCellEdited = { rowIndex: event.rowIndex, column: event.column, rowPinned: undefined };
    }
    (_c = this._inProgressChanges) === null || _c === void 0 ? void 0 : _c.forEach(({ resolve }) => resolve(newValue));
  }
  setCellEditors(customEditors) {
    var _a;
    this._customEditors = customEditors;
    (_a = this._gridOptions) === null || _a === void 0 ? void 0 : _a.api.stopEditing();
  }
  setCellRenders(customRenders) {
    this._customRenders = customRenders;
  }
  pushInvalidCell(invalidField, recordId) {
    this._invalidCells.push({ field: invalidField, recordId });
    this._nextInvalidCell = this._invalidCells[0];
  }
  getInvalidCells() {
    return this._invalidCells;
  }
  getNextInvalidCell() {
    return this._nextInvalidCell;
  }
  async processContinuousInsert() {
    if (!this.canContinuousInsert())
      return;
    this._dataUnit.addRecord();
  }
  clearInvalidCells(stopEdition) {
    this._invalidCells = [];
    this._nextInvalidCell = undefined;
    if (stopEdition) {
      this._isGridEdition = false;
    }
  }
  setEnableContinuousInsert(enable) {
    this._enableContinuousInsert = enable;
  }
  hasInvalidField(fieldName, recordId) {
    if (!this.hasInvalidCell())
      return false;
    this._nextInvalidCell = this._invalidCells[0];
    const containsFieldName = this._nextInvalidCell.field.name === fieldName;
    const containsRecordId = this._nextInvalidCell.recordId === recordId;
    return containsFieldName && containsRecordId;
  }
  hasInvalidCell() {
    var _a, _b, _c;
    const hasInvalidCell = ((_a = this._invalidCells) === null || _a === void 0 ? void 0 : _a.length) > 0;
    const hasField = (_b = this._invalidCells) === null || _b === void 0 ? void 0 : _b.filter(invalidCell => { invalidCell.field; });
    const hasRecordId = (_c = this._invalidCells) === null || _c === void 0 ? void 0 : _c.filter(invalidCell => { invalidCell.recordId; });
    if (!hasInvalidCell || !hasField || !hasRecordId)
      return false;
    return true;
  }
}
class TargetEdition {
  constructor(rowIndex, column, backwards) {
    this.rowPinned = null;
    this.rowIndex = rowIndex;
    this.column = column;
    this.backwards = backwards;
  }
}
