'use strict';

const core = require('@sankhyalabs/core');
const ApplicationUtils = require('./ApplicationUtils-5cc05979.js');
const index = require('./index-1be40f19.js');
const index$1 = require('./index-a7b0c73d.js');

const buildFieldMetadata = (descriptor, config) => {
  var _a, _b;
  let { name, label, group } = Object.assign({}, config);
  let { readOnly, required } = Object.assign({}, config);
  let props;
  let userInterface;
  if (descriptor) {
    label = label || descriptor.label;
    name = name || descriptor.name;
    required = (_a = descriptor.required) !== null && _a !== void 0 ? _a : config === null || config === void 0 ? void 0 : config.required;
    readOnly = (_b = descriptor.readOnly) !== null && _b !== void 0 ? _b : config === null || config === void 0 ? void 0 : config.readOnly;
    props = descriptor.properties;
    userInterface = descriptor.userInterface;
  }
  return {
    name, label, group,
    readOnly, required,
    props,
    userInterface: userInterface || core.UserInterface.SHORTTEXT,
  };
};

class RecordValidationProcessor {
  constructor(dataUnit, validationSource, validator) {
    this._dataUnit = dataUnit;
    this._validationSource = validationSource;
    this._validator = validator;
    this._i18n = index.getI18n();
  }
  validate(isSaving = true) {
    return new Promise((accept, reject) => {
      const records = this._dataUnit.getModifiedRecords();
      for (let i = 0; i < records.length; i++) {
        const record = records[i];
        const invalidResults = [];
        let validationResult = this.validateRequired(record);
        if (validationResult && !validationResult.isValid) {
          invalidResults.push(validationResult);
        }
        if (this._validator) {
          validationResult = this._validator.validateRecord(record);
          if (validationResult && !validationResult.isValid) {
            invalidResults.push(validationResult);
          }
        }
        if (invalidResults.length > 0) {
          if (!isSaving && invalidResults[0].invalidFields.length > 0) {
            this._validationSource.markAsInvalid(invalidResults[0].invalidFields[0], record.__record__id__);
            reject();
            break;
          }
          this.processValidationResult(invalidResults, record.__record__id__);
          reject();
          break;
        }
      }
      return accept();
    });
  }
  validateRequired(record) {
    const requiredFields = this._validationSource.getRequiredFields();
    const invalidFields = [];
    new Set(requiredFields).forEach(field => {
      const value = record[field];
      if (value == undefined || value === "") {
        const errorMessage = this._validationSource.getMessageForField(field, record.__record__id__);
        if (errorMessage) {
          invalidFields.push({ name: field, message: errorMessage });
        }
        else {
          invalidFields.push({ name: field, message: this._i18n('utils.requiredField') });
        }
      }
    });
    if (invalidFields.length > 0) {
      return {
        isValid: false,
        invalidFields,
        infoMessage: this._i18n('utils.requiredFieldMessage')
      };
    }
    return undefined;
  }
  processValidationResult(validationResult, recordId) {
    validationResult.forEach(invalidResult => {
      const invalidFields = invalidResult.invalidFields;
      this._dataUnit.savingCanceled(invalidFields, recordId);
      if (invalidFields) {
        invalidFields.forEach(field => {
          this.markAsInvalid(field, recordId);
        });
      }
      if (invalidResult.infoMessage) {
        ApplicationUtils.ApplicationUtils.info(invalidResult.infoMessage);
      }
      if (invalidResult.errorMessage) {
        const { errorTitle, errorMessage } = invalidResult;
        ApplicationUtils.ApplicationUtils.error(errorTitle, errorMessage);
      }
    });
  }
  markAsInvalid(field, recordId) {
    this._dataUnit.setInvalidField(field.name, field.message, recordId);
    this._validationSource.markAsInvalid(field, recordId);
  }
}

class DataBinder {
  constructor(dataUnit) {
    this.onDataUnitEvent = (action) => {
      var _a;
      switch (action.type) {
        case core.Action.MULTIPLE_EDITION_CHANGED:
          this.clearInvalid();
          this.updateAllFieldsValues();
          break;
        case core.Action.DATA_LOADED:
        case core.Action.DATA_SAVED:
        case core.Action.RECORDS_REMOVED:
        case core.Action.RECORDS_ADDED:
        case core.Action.RECORDS_COPIED:
        case core.Action.EDITION_CANCELED:
        case core.Action.SELECTION_CHANGED:
        case core.Action.NEXT_SELECTED:
        case core.Action.PREVIOUS_SELECTED:
          this.clearInvalid();
        case core.Action.DATA_CHANGED:
        case core.Action.CHANGE_UNDONE:
        case core.Action.CHANGE_REDONE:
        case core.Action.RECORD_LOADED:
          this.updateAllFieldsValues();
          this.clearFieldError(Object.keys(action.payload)[0]);
          break;
        case core.Action.FIELD_INVALIDATED:
          (_a = this._fields) === null || _a === void 0 ? void 0 : _a.forEach(field => {
            this.updateErrorMessage(field.fieldName, field.field);
          });
          break;
      }
    };
    this._uuid = core.StringUtils.generateUUID();
    this._fields = new Map();
    this._dataUnit = dataUnit;
    this.applyDefaultValues();
    this._dataUnit.subscribe(this.onDataUnitEvent);
    this._dataUnit.addInterceptor(this);
    this.setContextDataBinder(dataUnit);
  }
  get dataBinderId() {
    return this._uuid;
  }
  setContextDataBinder(dataUnit) {
    const dataBindersByDataUnit = core.ApplicationContext.getContextValue("__DATABINDER_BY_DATAUNIT__") || new Map();
    const dataBinders = [...(dataBindersByDataUnit.get(dataUnit.dataUnitId) || []), this];
    dataBindersByDataUnit.set(dataUnit.dataUnitId, dataBinders);
    core.ApplicationContext.setContextValue("__DATABINDER_BY_DATAUNIT__", dataBindersByDataUnit);
  }
  applyDefaultValues() {
    const recordIds = (this._dataUnit.getAddedRecords() || []).map(r => r.__record__id__);
    if (recordIds.length > 0) {
      const defaultValues = this.getDefaultValues();
      if (defaultValues) {
        Object.keys(defaultValues).forEach(field => {
          this._dataUnit.setFieldValue(field, defaultValues[field], recordIds);
        });
      }
    }
  }
  bind(fields, currentContextName, formMetadata, recordsValidator) {
    fields.forEach(fieldElement => {
      const { fieldName, contextName } = fieldElement.dataset;
      if (contextName == undefined || contextName === currentContextName) {
        this.updateBind(fieldName, fieldElement);
      }
    });
    this._formMetadata = formMetadata;
    this._recordValidatorProcessor = new RecordValidationProcessor(this._dataUnit, {
      getRequiredFields: () => this.getFormRequiredFields(),
      markAsInvalid: field => this.markInvalid(field),
      getMessageForField: field => this.getErrorMessage(field),
    }, recordsValidator);
  }
  getFormRequiredFields() {
    return this._formMetadata.getRequiredFields();
  }
  disconnectDataUnit() {
    const dataBindersByDataUnit = core.ApplicationContext.getContextValue("__DATABINDER_BY_DATAUNIT__") || new Map();
    const dataBinders = dataBindersByDataUnit.get(this._dataUnit.dataUnitId);
    const dataBindersFiltered = dataBinders.filter((dataBinder) => dataBinder.dataBinderId !== this.dataBinderId);
    dataBindersByDataUnit.set(this._dataUnit.dataUnitId, dataBindersFiltered);
    core.ApplicationContext.setContextValue("__DATABINDER_BY_DATAUNIT__", dataBindersByDataUnit);
  }
  onDisconnectedCallback() {
    this._dataUnit.unsubscribe(this.onDataUnitEvent);
    this._dataUnit.removeInterceptor(this);
    this.disconnectDataUnit();
  }
  getCurrentRecordId() {
    const record = this._dataUnit.getSelectedRecord();
    return record === null || record === void 0 ? void 0 : record.__record__id__;
  }
  markInvalid(field) {
    if (this._fields.has(field.name)) {
      const fieldElement = this._fields.get(field.name).field;
      this.updateErrorMessage(field.name, fieldElement, field.message);
    }
  }
  setFocus(fieldName) {
    if (!this._fields.has(fieldName)) {
      return;
    }
    const fieldElement = this._fields.get(fieldName).field;
    if (typeof fieldElement["setFocus"] != "function") {
      return;
    }
    fieldElement["setFocus"]();
  }
  clearInvalid(recordId) {
    this._dataUnit.clearInvalid(recordId);
    this._fields.forEach(fieldBinder => {
      const fieldElement = fieldBinder.field;
      fieldElement["errorMessage"] = "";
    });
  }
  clearFieldError(fieldName) {
    var _a;
    const field = (_a = this._fields.get(fieldName)) === null || _a === void 0 ? void 0 : _a.field;
    if (field && field["errorMessage"]) {
      field["errorMessage"] = "";
      this._dataUnit.clearInvalid(this.getCurrentRecordId(), fieldName);
    }
  }
  updateFieldValue(fieldName, field) {
    const bind = this._fields.get(fieldName);
    try {
      if (bind)
        bind.listen = false;
      if (this._dataUnit.isMultipleEdition && this.hasMultipleValuesBetweenRecords(fieldName)) {
        field['value'] = undefined;
        field['alternativePlaceholder'] = 'Múltiplos Valores';
      }
      else {
        field["value"] = this._dataUnit.getFieldValue(fieldName);
        field['alternativePlaceholder'] = undefined;
      }
      this.updateErrorMessage(fieldName, field);
    }
    finally {
      if (bind) {
        bind.listen = true;
      }
    }
  }
  hasMultipleValuesBetweenRecords(key) {
    var _a, _b;
    const selectedRecords = (_b = (_a = this._dataUnit.getSelectionInfo()) === null || _a === void 0 ? void 0 : _a.records) !== null && _b !== void 0 ? _b : [];
    if (selectedRecords.length <= 1)
      return false;
    const firstValue = selectedRecords[0][key];
    return !selectedRecords.every(record => core.ObjectUtils.equals(record[key], firstValue));
  }
  validate() {
    return this._recordValidatorProcessor.validate();
  }
  static async validateByDataunit(dataUnit) {
    try {
      const dataBinders = this.getDataBindersByDataUnit(dataUnit);
      await Promise.all(dataBinders.map((dataBinder) => dataBinder.validate()));
      return true;
    }
    catch (_a) {
      return false;
    }
  }
  static getDataBindersByDataUnit(dataUnit) {
    const dataBindersByDataUnit = core.ApplicationContext.getContextValue("__DATABINDER_BY_DATAUNIT__");
    return (dataBindersByDataUnit === null || dataBindersByDataUnit === void 0 ? void 0 : dataBindersByDataUnit.get(dataUnit.dataUnitId)) || [];
  }
  updateErrorMessage(fieldName, field, message) {
    if (message == undefined) {
      message = this._dataUnit.getInvalidMessage(this.getCurrentRecordId(), fieldName);
    }
    if (!field["errorMessage"]) {
      field["errorMessage"] = message;
    }
  }
  getErrorMessage(fieldName) {
    if (this._fields.has(fieldName)) {
      const fieldElement = this._fields.get(fieldName).field;
      return fieldElement["errorMessage"];
    }
    return undefined;
  }
  updateBind(fieldName, field) {
    const oldBind = this._fields.get(fieldName);
    if (oldBind) {
      oldBind.destroy();
    }
    this.bindSearchOptionsLoader(fieldName, field);
    this.updateFieldValue(fieldName, field);
    this.updateErrorMessage(fieldName, field);
    this._fields.set(fieldName, Bind.create(fieldName, field, (fieldName, waitingChange) => this.changeStarted(fieldName, waitingChange), (fieldName) => this.cancelWaitingChange(fieldName), (fieldName, newValue) => this.setFieldValue(fieldName, newValue)));
    this.applyEzUploadContext(fieldName, field);
  }
  changeStarted(fieldName, waitingChange) {
    /**
     * se o waitingChange não é blocante, add um event listener pra
     * fazer o accept da promise
     *
     * se não tiver promise no waitingChange, eu crio uma e resolvo no bind
     *
     *
     */
    if (this._dataUnit.records.length === 0) {
      this._dataUnit.addRecord();
    }
    if (!waitingChange.blocking && waitingChange.promise == undefined) {
      const bind = this._fields.get(fieldName);
      if (bind) {
        waitingChange.promise = new Promise((resolve, reject) => {
          bind.waitingChangePromiseResolve = resolve;
          bind.waitingChangePromiseReject = reject;
        });
      }
    }
    this._dataUnit.startChange(fieldName, waitingChange);
  }
  cancelWaitingChange(fieldName) {
    if (this._dataUnit.waitingForChange(fieldName)) {
      this._dataUnit.cancelWaitingChange(fieldName);
      const bind = this._fields.get(fieldName);
      if (bind) {
        bind.rejectWaitingChange(new core.WaitingChangeException("Change canceled", fieldName));
      }
    }
  }
  setFieldValue(fieldName, newValue) {
    if (this._dataUnit.records.length === 0) {
      this._dataUnit.addRecord();
    }
    this._dataUnit.clearInvalid(this.getCurrentRecordId(), fieldName);
    if (this._dataUnit.isMultipleEdition) {
      const selectionInfo = this._dataUnit.getSelectionInfo();
      this._dataUnit.setFieldValue(fieldName, newValue, selectionInfo.recordIds);
    }
    else {
      const record = this._dataUnit.getSelectedRecord();
      this._dataUnit.setFieldValue(fieldName, newValue, record ? [record.__record__id__] : undefined);
    }
    if (this._dataUnit.waitingForChange(fieldName)) {
      const bind = this._fields.get(fieldName);
      if (bind) {
        bind.acceptWaitingChange();
      }
    }
  }
  bindSearchOptionsLoader(fieldName, field) {
    if ((field.nodeName === "EZ-SEARCH" || field.nodeName === "EZ-SEARCH-PLUS") && field["optionLoader"] == undefined) {
      const isSearchPlus = field.nodeName === "EZ-SEARCH-PLUS";
      const loader = isSearchPlus ? core.ApplicationContext.getContextValue("__EZUI__SEARCH__PLUS__OPTION__LOADER__") : core.ApplicationContext.getContextValue("__EZUI__SEARCH__OPTION__LOADER__");
      if (loader) {
        field["optionLoader"] = (arg, ctxOptions) => {
          return loader(arg, fieldName, this._dataUnit, ctxOptions);
        };
      }
    }
  }
  applyEzUploadContext(fieldName, field) {
    var _a, _b;
    if (field.nodeName === "EZ-UPLOAD") {
      field["urlUpload"] = core.ApplicationContext.getContextValue("__EZUI__UPLOAD__ADD__URL__");
      field["urlDelete"] = core.ApplicationContext.getContextValue("__EZUI__UPLOAD__DEL__URL__");
      const descriptor = this._dataUnit.getField(fieldName);
      const destination = (_a = descriptor.properties) === null || _a === void 0 ? void 0 : _a.DESTINATION;
      if (destination) {
        field["requestHeaders"] = { XTRAINF: `{"destination": "${destination}"}` };
      }
      field["maxFiles"] = ((_b = descriptor.properties) === null || _b === void 0 ? void 0 : _b.MAX_FILES) || 0;
    }
  }
  updateAllFieldsValues() {
    var _a;
    (_a = this._fields) === null || _a === void 0 ? void 0 : _a.forEach(field => {
      this.updateFieldValue(field.fieldName, field.field);
    });
  }
  interceptAction(action) {
    if (action.type === core.Action.RECORDS_COPIED) {
      const cleanFields = this._formMetadata.getCleanOnCopyFields();
      if (cleanFields) {
        const records = action.payload;
        return new core.DataUnitAction(core.Action.RECORDS_COPIED, records.map(record => {
          const newRecord = Object.assign({}, record);
          cleanFields.forEach(fieldName => delete newRecord[fieldName]);
          return newRecord;
        }));
      }
    }
    if (action.type === core.Action.SAVING_DATA) {
      return new Promise((resolve) => {
        this.validate()
          .then(() => resolve(action))
          .catch(() => { });
      });
    }
    if (action.type === core.Action.RECORDS_ADDED) {
      const defaultValues = this.getDefaultValues();
      if (defaultValues) {
        const records = action.payload;
        return new core.DataUnitAction(core.Action.RECORDS_ADDED, records.map(record => {
          return Object.assign(Object.assign({}, record), defaultValues);
        }));
      }
    }
    return action;
  }
  getDefaultValues() {
    var _a;
    const rawDefaultValues = (_a = this._formMetadata) === null || _a === void 0 ? void 0 : _a.getDefaultValues();
    if (rawDefaultValues) {
      const defaultValues = {};
      for (const field in rawDefaultValues) {
        defaultValues[field] = this._dataUnit.valueFromString(field, rawDefaultValues[field]);
      }
      return defaultValues;
    }
    return undefined;
  }
}
class Bind {
  constructor() {
    this.listen = true;
    this.startChangeEventName = "ezStartChange";
    this.cancelWaitingChangeEventName = "ezCancelWaitingChange";
    this.changeEventName = "ezChange";
  }
  destroy() {
    this.field.removeEventListener(this.startChangeEventName, this.startChangeListener);
    this.field.removeEventListener(this.cancelWaitingChangeEventName, this.cancelWaitingChangeListener);
    this.field.removeEventListener(this.changeEventName, this.changeListener);
  }
  acceptWaitingChange() {
    if (this.waitingChangePromiseResolve) {
      this.waitingChangePromiseResolve();
      this.waitingChangePromiseReject = undefined;
      this.waitingChangePromiseResolve = undefined;
    }
  }
  rejectWaitingChange(reason) {
    if (this.waitingChangePromiseReject) {
      this.waitingChangePromiseReject(reason);
      this.waitingChangePromiseReject = undefined;
      this.waitingChangePromiseResolve = undefined;
    }
  }
  static create(fieldName, field, startChangeCallback, cancelWaitingChangeCallback, changeCallback) {
    const b = new Bind();
    b.field = field;
    b.fieldName = fieldName;
    b.startChangeListener = (evt) => { if (b.listen)
      startChangeCallback(fieldName, evt.detail); };
    b.field.addEventListener(b.startChangeEventName, b.startChangeListener);
    b.cancelWaitingChangeListener = () => { if (b.listen)
      cancelWaitingChangeCallback(fieldName); };
    b.field.addEventListener(b.cancelWaitingChangeEventName, b.cancelWaitingChangeListener);
    b.changeListener = (evt) => { if (b.listen)
      changeCallback(fieldName, evt.detail); };
    b.field.addEventListener(b.changeEventName, b.changeListener);
    return b;
  }
}

const SHORTCUT_SEARCH_FIELD = "ctrl+f";
const SEARCH_FIELD_FULL_WIDTH = 240;
const SEARCH_FIELD_MARGIN_BOTTOM = "0px";
const SEARCH_FIELD_WIDTH = String(SEARCH_FIELD_FULL_WIDTH - 30) + "px";
const buildFieldSearch = (action, optionLoader, onSelectField) => {
  return index$1.h("ez-search", { class: 'ez-padding--small ez-margin--none', style: {
      "--ez-text-input__margin-bottom": SEARCH_FIELD_MARGIN_BOTTOM,
      "--ez-search--width": SEARCH_FIELD_WIDTH
    }, canShowError: false, showSelectedValue: false, showOptionValue: false, suppressEmptyOption: true, label: action.label, value: null, optionLoader: optionLoader, onEzChange: (evt) => onSelectField(evt.detail), ignoreLimitCharsToSearch: true });
};
const focusOnFieldSerch = async (field) => {
  requestAnimationFrame(async () => {
    if (field) {
      const realElement = field['$elm$'];
      if (!realElement)
        return;
      realElement.value = null;
      await realElement.setFocus();
    }
  });
};

exports.DataBinder = DataBinder;
exports.RecordValidationProcessor = RecordValidationProcessor;
exports.SEARCH_FIELD_FULL_WIDTH = SEARCH_FIELD_FULL_WIDTH;
exports.SHORTCUT_SEARCH_FIELD = SHORTCUT_SEARCH_FIELD;
exports.buildFieldMetadata = buildFieldMetadata;
exports.buildFieldSearch = buildFieldSearch;
exports.focusOnFieldSerch = focusOnFieldSerch;
