import { ApplicationContext, ObjectUtils } from '@sankhyalabs/core';
import { buildFormConfigFromDataUnit } from '@sankhyalabs/ezui/dist/collection/utils/form';
import { h, Host } from '@stencil/core';
import { ApplicationUtils } from '@sankhyalabs/ezui/dist/collection/utils';
import { UserConfigType } from '../../lib/http/data-fetcher/fetchers/form-config-fetcher';
import { GROUP_NAMES, TAB_NAMES } from '../../lib/utils/constants';
import { buildChildrenGuides, buildGuides, buildGuidesMap, buildNewGuideName, getFieldsToSave, updateTabInFieldsFromGroupList, } from './FormConfigHelper';
export class SnkFormConfig {
  constructor() {
    this.guidesMap = new Map();
    this.availableFields = [];
    this.guidesList = [];
    this.groupsList = [];
    this.selectedGuide = undefined;
    this._formConfig = {};
    this.configOptions = [];
    this.originalConfigSelected = undefined;
    this.configSelected = undefined;
    this.hasChanges = false;
    this.optionConfigChanged = false;
    this.dataUnit = undefined;
    this.configManager = undefined;
    this.ignoreReadOnlyFormFields = undefined;
    this.messagesBuilder = undefined;
  }
  handleFieldConfigChanged() {
    this.hasChanges = true;
  }
  async handleFormConfigOptionSelected({ detail }) {
    this.configSelected = detail;
    await this.loadConfigByUser();
    this.selectedGuide = undefined;
    this.optionConfigChanged = !ObjectUtils.equals(this.configSelected, this.originalConfigSelected);
    if (this.configSelected.origin === UserConfigType.DEFAULT) {
      this.hasChanges = false;
    }
  }
  async handleAddFieldToGuide({ detail: fieldItem }) {
    var _a;
    if (!this.selectedGuide) {
      this.showNoGuideSelectedDialog();
      return;
    }
    if (this.selectedGuide.isCustom || this.selectedGuide.isDetail) {
      this.showCanNotAddFieldToGuideDialog();
      return;
    }
    this.availableFields = [...this.availableFields.filter(f => f.name !== fieldItem.name)];
    await ((_a = this.refFieldsLayout) === null || _a === void 0 ? void 0 : _a.addFieldToLayout(fieldItem));
  }
  async handleSetFieldAsAvailable({ detail }) {
    const hasField = this.availableFields.some(f => f.name === detail.name);
    if (hasField)
      return;
    this.availableFields = [...this.availableFields, detail];
  }
  async handleRemoveFieldFromAvailable({ detail }) {
    this.availableFields = this.availableFields.filter(f => f.name !== detail.name);
  }
  observeSelectedGuide(guide) {
    var _a;
    const guideGroups = (_a = this.guidesMap.get(guide === null || guide === void 0 ? void 0 : guide.name)) !== null && _a !== void 0 ? _a : [];
    this.groupsList = [...guideGroups];
  }
  observeConfigManager() {
    this.loadFormConfig();
  }
  showNoGuideSelectedDialog() {
    const title = this.getMessage('snkFormConfig.noGuideSelected.title');
    const message = this.getMessage('snkFormConfig.noGuideSelected.message');
    ApplicationUtils.alert(title, message);
  }
  showCanNotAddFieldToGuideDialog() {
    const title = this.getMessage('snkFormConfig.canNotAddFieldToGuide.title');
    const message = this.getMessage('snkFormConfig.canNotAddFieldToGuide.message');
    ApplicationUtils.alert(title, message);
  }
  /**
   * 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-form.msg.ts"
   */
  getMessage(key, params) {
    return this.messagesBuilder.getMessage(key, params);
  }
  async initializeUserConfig() {
    if (this.configManager == undefined)
      return;
    try {
      const userConfig = await this.configManager.fetchUserAvailableConfigs();
      this.configOptions = userConfig;
      const isDefault = this._formConfig != undefined ? this._formConfig.defaultConfiguration : true;
      const origin = (isDefault ? UserConfigType.DEFAULT : UserConfigType.USER);
      this.configSelected = userConfig.find((config) => config.origin === origin);
      this.originalConfigSelected = userConfig.find((config) => config.origin === origin);
    }
    catch (error) {
      console.error(this.getMessage('snkFormConfig.errors.failFetchUserConfig'));
      console.error(error);
    }
  }
  async loadConfigByUser() {
    if (!this.configManager || !this.configSelected)
      return;
    if (this.isConfigDefaultSelected()) {
      await this.handleLoadDefaultConfig();
    }
    else {
      this.loadFormConfig();
    }
  }
  async handleLoadDefaultConfig() {
    const defaultConfig = await this.configManager.fetchDefaultConfig();
    if (defaultConfig) {
      this.loadFormConfig(defaultConfig);
      return;
    }
    const emptyConfig = this.configManager.getEmptyConfig();
    if (emptyConfig) {
      this.loadFormConfig(emptyConfig);
    }
  }
  isConfigDefaultSelected() {
    return this.configSelected.origin === UserConfigType.DEFAULT;
  }
  loadFormConfig(config) {
    this._formConfig = config ? config : this.getConfig();
    this.loadGuides();
    this.initializeAvailableFields();
  }
  initializeAvailableFields() {
    var _a;
    if (((_a = this._formConfig) === null || _a === void 0 ? void 0 : _a.fields) == undefined)
      return;
    const fieldsFormConfig = [...this._formConfig.fields];
    const fieldsMetadata = this.dataUnit.metadata.fields;
    this.availableFields = fieldsMetadata.filter(({ name: fieldName, visible: fieldVisible, properties }) => {
      const hasCfg = fieldsFormConfig.some(({ name: fieldFormName }) => fieldFormName === fieldName);
      /**
       * Quando a visibilidade de um campo é alterada pelo dataunit, mas o desenvolvedor deseja que o mesmo continue
       * sendo apresentado no configurador, precisamos fazer essa tratativa, verificando se o campo não tem configuração
       */
      if (!hasCfg && properties.visibleOnConfig === true && !fieldVisible)
        return properties.visibleOnConfig;
      return fieldVisible === true && hasCfg === false;
    });
  }
  getInitialOrder(guidesList) {
    var _a, _b;
    if (Array.isArray((_a = this._formConfig) === null || _a === void 0 ? void 0 : _a.tabs)) {
      return (_b = this._formConfig.tabs) === null || _b === void 0 ? void 0 : _b.length;
    }
    if (guidesList.length > 0) {
      return Math.max(...guidesList.map(g => g.order));
    }
    return 0;
  }
  loadGuides() {
    var _a;
    this.guidesList = [];
    this.guidesList.push(...buildGuides(this._formConfig, this.getMessage('snkFormConfig.form.mainArea')));
    this.guidesList.push(...buildChildrenGuides(this.dataUnit, this.getInitialOrder(this.guidesList), this.guidesList));
    this.guidesMap = buildGuidesMap((_a = this._formConfig) === null || _a === void 0 ? void 0 : _a.fields, this.guidesList, this.dataUnit, this.getMessage('snkFormConfig.form.tabGeneral'));
  }
  getConfig() {
    let config = this.configManager.getConfig(this.dataUnit, false);
    if (config.fields && config.fields.length === 0) {
      config = undefined;
    }
    if (config == undefined) {
      config = buildFormConfigFromDataUnit(this.dataUnit);
    }
    return ObjectUtils.copy(config);
  }
  handleSelectGuide({ detail: selectedGuide }) {
    this.selectedGuide = selectedGuide;
  }
  handleLayoutChanged({ detail: newGroupList }) {
    this.hasChanges = true;
    this.groupsList = [...newGroupList];
    this.guidesMap.set(this.selectedGuide.name, [...newGroupList]);
  }
  getIsDefaultConfig() {
    var _a;
    const optionSelected = (_a = this.configSelected) === null || _a === void 0 ? void 0 : _a.origin;
    return optionSelected === UserConfigType.DEFAULT
      && this.optionConfigChanged === true
      && this.hasChanges === false;
  }
  getTabsToSave() {
    return this.guidesList.map(tab => {
      return {
        name: tab.name,
        label: tab.name === TAB_NAMES.main ? tab.name : tab.label,
        order: tab.name === TAB_NAMES.main ? 0 : tab.order,
        visible: tab.visible,
      };
    });
  }
  async handleSaveConfig() {
    var _a;
    const savedConfig = await this.configManager.saveConfig(this.buildConfigToSave());
    this.configSelected = (_a = this.configOptions) === null || _a === void 0 ? void 0 : _a.find((config) => {
      return config.origin === (this.hasChanges ? UserConfigType.USER : UserConfigType.DEFAULT);
    });
    this.originalConfigSelected = this.configSelected;
    this.hasChanges = false;
    this.optionConfigChanged = false;
    ApplicationUtils.info(this.getMessage('snkFormConfig.info.successfullyConfigSaved'), { iconName: 'check' });
    this.configChange.emit(savedConfig);
  }
  buildConfigToSave() {
    if (this.getIsDefaultConfig()) {
      const formConfig = this._formConfig;
      formConfig.defaultConfiguration = true;
      return formConfig;
    }
    const formConfig = ObjectUtils.copy(this._formConfig);
    formConfig.tabs = this.getTabsToSave();
    formConfig.fields = getFieldsToSave(this.guidesMap);
    formConfig.defaultConfiguration = false;
    return formConfig;
  }
  handleAvailableFieldListChanged({ detail: updatedList }) {
    this.availableFields = [...updatedList];
  }
  handleSetFieldListAsAvailable({ detail: updatedList }) {
    this.availableFields = [...this.availableFields, ...updatedList];
  }
  handleGuideDeleted({ detail: guide }) {
    var _a, _b;
    const guideName = guide.name;
    const guideGroups = (_a = this.guidesMap.get(guideName)) !== null && _a !== void 0 ? _a : [];
    const fieldsToAvailable = [];
    guideGroups.forEach(group => fieldsToAvailable.push(...group.fields));
    this.availableFields = [...this.availableFields, ...fieldsToAvailable];
    this.guidesList = [...this.guidesList.filter(g => g.name !== guideName)];
    this.guidesMap.delete(guideName);
    if (((_b = this.selectedGuide) === null || _b === void 0 ? void 0 : _b.name) === guideName) {
      this.selectedGuide = undefined;
    }
    this.hasChanges = true;
  }
  handleGuideListChanged({ detail: updatedList }) {
    this.hasChanges = true;
    this.guidesList = [...updatedList];
  }
  handleGuideRenamed({ detail: updatedName }) {
    var _a;
    const guideGroups = (_a = this.guidesMap.get(this.selectedGuide.name)) !== null && _a !== void 0 ? _a : [];
    const updatedGroups = updateTabInFieldsFromGroupList(guideGroups, updatedName);
    this.guidesMap.delete(this.selectedGuide.name);
    this.guidesMap.set(updatedName, updatedGroups);
    const updatedGuide = {
      name: updatedName,
      label: updatedName,
      visible: this.selectedGuide.visible,
      order: this.selectedGuide.order,
    };
    this.guidesList = [...this.guidesList.map(guide => (guide.name === this.selectedGuide.name) ? updatedGuide : guide)];
    this.groupsList = [...updatedGroups];
    this.selectedGuide = updatedGuide;
    this.hasChanges = true;
  }
  handleCreateNewGuide() {
    const newGuideName = buildNewGuideName(this.guidesList, this.getMessage.bind(this));
    const newGuide = {
      name: newGuideName,
      label: newGuideName,
      visible: true,
      order: this.guidesList.length + 1,
    };
    const newGuideFixedGroup = {
      name: GROUP_NAMES.noGroup,
      fields: [],
    };
    this.guidesList = [...this.guidesList, newGuide];
    this.guidesMap.set(newGuideName, [newGuideFixedGroup]);
    this.selectedGuide = newGuide;
    this.hasChanges = true;
  }
  getGuideNames() {
    return [
      ...this.guidesList.map(g => g.name.toLowerCase()),
      this.getMessage('snkFormConfig.form.mainArea').toLowerCase(),
    ];
  }
  async componentWillRender() {
    if (this.messagesBuilder == undefined) {
      const application = ApplicationContext.getContextValue('__SNK__APPLICATION__');
      this.messagesBuilder = application.messagesBuilder;
    }
  }
  async componentWillLoad() {
    this.loadFormConfig();
    await this.initializeUserConfig();
  }
  render() {
    return (h(Host, null, h("config-header", { configOptions: this.configOptions, selectedConfig: this.configSelected, messagesBuilder: this.messagesBuilder, hasChanges: this.hasChanges, optionConfigChanged: this.optionConfigChanged, onConfigClose: () => this.configClose.emit(), onSaveConfig: async () => await this.handleSaveConfig() }), h("div", { class: "ez-padding--medium" }, h("div", { class: "ez-flex sidebarNavigator__container" }, h("guides-configurator", { messagesBuilder: this.messagesBuilder, guidesList: this.guidesList, selectedGuide: this.selectedGuide, onGuideSelected: this.handleSelectGuide.bind(this), onGuideListChanged: this.handleGuideListChanged.bind(this), onCreateNewGuide: this.handleCreateNewGuide.bind(this), onGuideDeleted: this.handleGuideDeleted.bind(this) }), h("fields-layout", { ref: ref => this.refFieldsLayout = ref, selectedGuide: this.selectedGuide, guideNames: this.getGuideNames(), groupsList: this.groupsList, messagesBuilder: this.messagesBuilder, dataUnit: this.dataUnit, onGuideRenamed: this.handleGuideRenamed.bind(this), onLayoutChanged: this.handleLayoutChanged.bind(this), onSetFieldListAsAvailable: this.handleSetFieldListAsAvailable.bind(this) }), h("fields-selector", { dataUnit: this.dataUnit, availableFields: this.availableFields, onFieldListChanged: this.handleAvailableFieldListChanged.bind(this), messagesBuilder: this.messagesBuilder })))));
  }
  static get is() { return "snk-form-config"; }
  static get encapsulation() { return "scoped"; }
  static get originalStyleUrls() {
    return {
      "$": ["snk-form-config.css"]
    };
  }
  static get styleUrls() {
    return {
      "$": ["snk-form-config.css"]
    };
  }
  static get properties() {
    return {
      "dataUnit": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "DataUnit",
          "resolved": "DataUnit",
          "references": {
            "DataUnit": {
              "location": "import",
              "path": "@sankhyalabs/core"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Reposit\u00F3rio de dados, controla a manipula\u00E7\u00E3o dos dados."
        }
      },
      "configManager": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "SnkFormConfigManager",
          "resolved": "SnkFormConfigManager",
          "references": {
            "SnkFormConfigManager": {
              "location": "import",
              "path": "./SnkFormConfigManager"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Campos de configura\u00E7\u00E3o de formul\u00E1rio."
        }
      },
      "ignoreReadOnlyFormFields": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": true,
        "docs": {
          "tags": [],
          "text": "Ignora os campos \"somente leitura\" no modo de inser\u00E7\u00E3o."
        },
        "attribute": "ignore-read-only-form-fields",
        "reflect": false
      },
      "messagesBuilder": {
        "type": "unknown",
        "mutable": true,
        "complexType": {
          "original": "SnkMessageBuilder",
          "resolved": "SnkMessageBuilder",
          "references": {
            "SnkMessageBuilder": {
              "location": "import",
              "path": "../../lib/message/SnkMessageBuilder"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Respons\u00E1vel por flexibilizar e padronizar o uso de mensagens nos blocos de constru\u00E7\u00E3o."
        }
      }
    };
  }
  static get states() {
    return {
      "availableFields": {},
      "guidesList": {},
      "groupsList": {},
      "selectedGuide": {},
      "_formConfig": {},
      "configOptions": {},
      "originalConfigSelected": {},
      "configSelected": {},
      "hasChanges": {},
      "optionConfigChanged": {}
    };
  }
  static get events() {
    return [{
        "method": "configClose",
        "name": "configClose",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Emitido ao fechar as configura\u00E7\u00F5es."
        },
        "complexType": {
          "original": "void",
          "resolved": "void",
          "references": {}
        }
      }, {
        "method": "configChange",
        "name": "configChange",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Emitido ao salvar as configura\u00E7\u00F5es."
        },
        "complexType": {
          "original": "IFormConfig",
          "resolved": "IFormConfig",
          "references": {
            "IFormConfig": {
              "location": "import",
              "path": "@sankhyalabs/ezui/dist/types/utils/form/interfaces"
            }
          }
        }
      }];
  }
  static get watchers() {
    return [{
        "propName": "selectedGuide",
        "methodName": "observeSelectedGuide"
      }, {
        "propName": "configManager",
        "methodName": "observeConfigManager"
      }];
  }
  static get listeners() {
    return [{
        "name": "fieldConfigChanged",
        "method": "handleFieldConfigChanged",
        "target": "body",
        "capture": false,
        "passive": false
      }, {
        "name": "formConfigOptionSelected",
        "method": "handleFormConfigOptionSelected",
        "target": "body",
        "capture": false,
        "passive": false
      }, {
        "name": "addFieldToGuide",
        "method": "handleAddFieldToGuide",
        "target": "body",
        "capture": false,
        "passive": false
      }, {
        "name": "setFieldAsAvailable",
        "method": "handleSetFieldAsAvailable",
        "target": "body",
        "capture": false,
        "passive": false
      }, {
        "name": "removeFieldFromAvailable",
        "method": "handleRemoveFieldFromAvailable",
        "target": "body",
        "capture": false,
        "passive": false
      }];
  }
}
