import { Action, DataUnit, ElementIDUtils, StringUtils, ApplicationContext, KeyboardManager } from "@sankhyalabs/core";
import { Host, forceUpdate, h } from "@stencil/core";
import { createStore } from "redux";
import DataBinder from "../../utils/form/DataBinder";
import { buildFormMetadata } from "../../utils/form/FormMetadata";
import { FormLayout } from '../../utils/form/interfaces';
import { changeTab, formReducer, loadMetadata, selectCurrentSheet, selectFormMetadata } from "./store/form.slice";
import { buildFieldSearch } from "../../utils";
import { focusOnFieldSerch, SEARCH_FIELD_FULL_WIDTH, SHORTCUT_SEARCH_FIELD } from "../../utils/searchColumn/search-column";
import initI18n from "../../utils/i18n";
export class EzForm {
  constructor() {
    this._customEditors = new Map();
    this._application = ApplicationContext.getContextValue("__SNK__APPLICATION__");
    this.onDataUnitAction = (action) => {
      if (action.type === Action.METADATA_LOADED) {
        this.processMetadata();
      }
    };
    this._fieldsProps = new Map();
    this._singleColumn = false;
    this.dataUnit = undefined;
    this.config = undefined;
    this.recordsValidator = undefined;
    this.fieldToFocus = undefined;
    this.onlyStaticFields = false;
    this.useSearchField = true;
    this.elementFocusSearchField = undefined;
  }
  /**
   * Realiza validação no conteúdo de todos os campos.
   */
  validate() {
    return this._dataBinder.validate();
  }
  /**
   * Registra um editor customizado para campos da grade e formulário.
   */
  async addCustomEditor(fieldName, customEditor, detailContext) {
    if (this._formView) {
      this._formView.addCustomEditor(fieldName, customEditor, detailContext);
      return;
    }
    const newCustomEditors = new Map(this._customEditors);
    newCustomEditors.set(fieldName, { customEditor, detailContext });
    this._customEditors = newCustomEditors;
  }
  observeConfig() {
    this.processMetadata();
  }
  /**
   * Altera/adiciona uma propriedade nos metadados do campo.
   */
  async setFieldProp(fieldName, propName, value) {
    const newCustomFieldProps = new Map(this._fieldsProps);
    const currentProps = this._fieldsProps.get(fieldName);
    newCustomFieldProps.set(fieldName, Object.assign(Object.assign({}, currentProps), { [propName]: value }));
    this._fieldsProps = newCustomFieldProps;
  }
  getDynamicContent() {
    var _a;
    const formMD = selectFormMetadata(this._store.getState());
    if (!formMD) {
      return null;
    }
    const allSheets = Array.from(formMD.getAllSheets().values());
    const currentSheet = selectCurrentSheet((_a = this._store) === null || _a === void 0 ? void 0 : _a.getState());
    let result = [];
    if (allSheets.length > 1) {
      const tabs = allSheets.map((sheet, index) => {
        return { tabKey: sheet.name, label: sheet.label, index };
      });
      const idTabSelector = `selector`;
      result.push(h("ez-tabselector", { tabs: this.buildIdTabSelector(tabs), onEzChange: (evt) => this._store.dispatch(changeTab(evt.detail)), selectedTab: currentSheet.name, "data-element-id": idTabSelector }));
    }
    result = result.concat(this.buildFormContent(currentSheet));
    return result;
  }
  buildFormContent(currentSheet) {
    const fields = currentSheet === null || currentSheet === void 0 ? void 0 : currentSheet.fields;
    if (currentSheet == undefined) {
      return;
    }
    this.ezFormSetFields.emit(fields);
    const idFormSheet = `${StringUtils.replaceAccentuatedChars(StringUtils.toCamelCase(currentSheet === null || currentSheet === void 0 ? void 0 : currentSheet.label), false)}_selectorContainer`;
    return (h("div", { class: "dynamic-content ez-box--no-outline", "data-element-id": idFormSheet, ref: elem => (this._container = elem), tabindex: "0" }, h("ez-popover", { ref: elem => (this._ezPopoverSearchField = elem), overlayType: "none" }, this.renderFieldColumn()), h("ez-form-view", { ref: (element) => (this._formView = element), class: "ez-row ez-padding-vertical--small", fields: fields, singleColumn: this._singleColumn, selectedRecord: this.dataUnit.getSelectedRecord() })));
  }
  renderFieldColumn() {
    if (this._fieldSearch != undefined) {
      return this._fieldSearch;
    }
    this._fieldSearch = buildFieldSearch({
      value: StringUtils.generateUUID(),
      label: this._i18n("ez-form.labelSearchField"),
    }, ({ argument }) => this.fieldsOptionLoader(argument), (option) => this.onSelectField(option));
    return this._fieldSearch;
  }
  getFormFields() {
    var _a;
    return (_a = this.config) === null || _a === void 0 ? void 0 : _a.fields;
  }
  fieldsOptionLoader(argument) {
    const searchTerm = argument === null || argument === void 0 ? void 0 : argument.toLowerCase();
    const allFields = this.getFormFields().map(field => { var _a; return (_a = this.dataUnit) === null || _a === void 0 ? void 0 : _a.getField(field.name); });
    const filteredFields = allFields.filter(field => {
      var _a, _b;
      return ((_a = field.name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(searchTerm)) ||
        ((_b = field.label) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(searchTerm));
    });
    const options = filteredFields.map(field => ({ value: field.name, label: field.label }));
    return Promise.resolve(options);
  }
  onSelectField(option) {
    if (option == undefined || option.value == undefined) {
      this.fieldToFocus = "";
      return;
    }
    this.fieldToFocus = option.value;
    this._ezPopoverSearchField.hide();
  }
  async initKeyboardManager() {
    var _a, _b;
    this._keyboardManager = new KeyboardManager({ propagate: false, element: (_a = this.elementFocusSearchField) !== null && _a !== void 0 ? _a : this._element });
    if (this.useSearchField) {
      this._keyboardManager.bind(SHORTCUT_SEARCH_FIELD, async () => {
        if (!this._container || !this._ezPopoverSearchField)
          return;
        const container = this._container.getBoundingClientRect();
        await focusOnFieldSerch(this._fieldSearch);
        this._ezPopoverSearchField.showUnder(this._container, { horizontalGap: container.width - (SEARCH_FIELD_FULL_WIDTH), verticalGap: container.height * -1 });
      }, { description: this._i18n("ez-form.labelSearchField"), element: (_b = this.elementFocusSearchField) !== null && _b !== void 0 ? _b : this._element });
    }
  }
  componentDidLoad() {
    this.initKeyboardManager();
  }
  processMetadata() {
    if (this.bindFields() && this.dataUnit && this._store) {
      const metadata = buildFormMetadata(this.config, this.dataUnit);
      this._store.dispatch(loadMetadata(metadata));
    }
  }
  isStatic() {
    var _a;
    return ((_a = this._staticFields) === null || _a === void 0 ? void 0 : _a.length) > 0;
  }
  bindFields() {
    return !this.isStatic() || this.onlyStaticFields === false;
  }
  async componentWillLoad() {
    var _a;
    if (this.dataUnit === undefined) {
      this.dataUnit = new DataUnit("ez-form");
    }
    this.dataUnit.unsubscribe(this.onDataUnitAction);
    this.dataUnit.subscribe(this.onDataUnitAction);
    this._dataBinder = new DataBinder(this.dataUnit);
    this._store = createStore(formReducer);
    this._store.subscribe(() => forceUpdate(this));
    this._staticFields = Array.from(this._element.querySelectorAll("[data-field-name]"));
    this.processMetadata();
    const dataInfo = { dataUnit: this.dataUnit };
    ElementIDUtils.addIDInfo(this._element, null, dataInfo);
    const layoutFormConfig = await ((_a = this._application) === null || _a === void 0 ? void 0 : _a.getLayoutFormConfig());
    this.setSingleColumn(layoutFormConfig);
    this.registerNotifyListeners(layoutFormConfig);
    this._i18n = await initI18n();
  }
  async setSingleColumn(layoutFormConfig) {
    this._singleColumn = (layoutFormConfig === null || layoutFormConfig === void 0 ? void 0 : layoutFormConfig.config) === FormLayout.CASCADE;
  }
  async registerNotifyListeners(layoutFormConfig) {
    if (!layoutFormConfig)
      return;
    layoutFormConfig.onConfigChange((config) => {
      if (config === FormLayout.CASCADE) {
        this._singleColumn = true;
      }
      else {
        this._singleColumn = false;
      }
    });
  }
  componentDidRender() {
    const metadata = selectFormMetadata(this._store.getState());
    metadata.addRequiredFields(this._staticFields.filter(f => f.dataset.required).map(f => f.dataset.fieldName));
    this._dataBinder.bind(Array.from(this._element.querySelectorAll("[data-field-name]")), this.dataUnit.dataUnitId, metadata, this.recordsValidator);
    this.ezReady.emit();
    this.handleFieldToFocus();
    this.setCustomEditors();
    this.setFieldsProps();
  }
  setCustomEditors() {
    if (!this._formView) {
      return;
    }
    for (const [fieldName, customEditorProps] of this._customEditors) {
      this._formView.addCustomEditor(fieldName, customEditorProps.customEditor, customEditorProps.detailContext);
      this._customEditors.delete(fieldName);
    }
  }
  handleFieldToFocus() {
    var _a;
    if (this.fieldToFocus == undefined) {
      return;
    }
    const currentSheet = selectCurrentSheet((_a = this._store) === null || _a === void 0 ? void 0 : _a.getState());
    const fields = currentSheet === null || currentSheet === void 0 ? void 0 : currentSheet.fields;
    if (!fields.some(item => item.name === this.fieldToFocus)) {
      return;
    }
    requestAnimationFrame(() => {
      this._dataBinder.setFocus(this.fieldToFocus);
      this.ezFormRequestClearFieldToFocus.emit();
      this.fieldToFocus = null;
    });
  }
  setFieldsProps() {
    if (!this._formView) {
      return;
    }
    for (const [fieldName, props] of this._fieldsProps) {
      for (const prop in props) {
        this._formView.setFieldProp(fieldName, prop, props[prop]);
      }
      this._fieldsProps.delete(fieldName);
    }
  }
  disconnectedCallback() {
    var _a;
    this.dataUnit.unsubscribe(this.onDataUnitAction);
    this._dataBinder.onDisconnectedCallback();
    (_a = this._keyboardManager) === null || _a === void 0 ? void 0 : _a.unbindAllShortcutKeys();
  }
  buildIdTabSelector(tabs) {
    if (tabs) {
      tabs.forEach(tab => tab[ElementIDUtils.DATA_ELEMENT_ID_ATTRIBUTE_NAME] = StringUtils.toCamelCase(tab.label));
    }
    return tabs;
  }
  render() {
    return (h(Host, null, this.isStatic() ? null : this.getDynamicContent()));
  }
  static get is() { return "ez-form"; }
  static get encapsulation() { return "scoped"; }
  static get originalStyleUrls() {
    return {
      "$": ["ez-form.css"]
    };
  }
  static get styleUrls() {
    return {
      "$": ["ez-form.css"]
    };
  }
  static get properties() {
    return {
      "dataUnit": {
        "type": "unknown",
        "mutable": true,
        "complexType": {
          "original": "DataUnit",
          "resolved": "DataUnit",
          "references": {
            "DataUnit": {
              "location": "import",
              "path": "@sankhyalabs/core"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Unidade de dados. Respons\u00E1vel pelo controle de edi\u00E7\u00E3o de registros e \ninforma\u00E7\u00F5es pertinentes aos campos."
        }
      },
      "config": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "IFormConfig",
          "resolved": "IFormConfig",
          "references": {
            "IFormConfig": {
              "location": "import",
              "path": "../../utils/form/interfaces"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Configura\u00E7\u00E3o do formul\u00E1rio."
        }
      },
      "recordsValidator": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "IRecordValidator",
          "resolved": "IRecordValidator",
          "references": {
            "IRecordValidator": {
              "location": "import",
              "path": "../../utils/form/interfaces"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Define um validador respons\u00E1vel pela integridade dos registros."
        }
      },
      "fieldToFocus": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Determina o campo que deve ficar em evid\u00EAncia."
        },
        "attribute": "field-to-focus",
        "reflect": false
      },
      "onlyStaticFields": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Define se os campos que ser\u00E3o apresentados s\u00E3o todos est\u00E1ticos. Quando verdadeira, ocorrer\u00E1 no DataBinder o bind dos campos com o DataUnit."
        },
        "attribute": "only-static-fields",
        "reflect": false,
        "defaultValue": "false"
      },
      "useSearchField": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Define se o formulario deve exibir um buscador de coluna com uso do Ctrl+F"
        },
        "attribute": "use-search-field",
        "reflect": false,
        "defaultValue": "true"
      },
      "elementFocusSearchField": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "HTMLElement",
          "resolved": "HTMLElement",
          "references": {
            "HTMLElement": {
              "location": "global"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Define uma ancoragem"
        }
      }
    };
  }
  static get states() {
    return {
      "_fieldsProps": {},
      "_singleColumn": {}
    };
  }
  static get events() {
    return [{
        "method": "ezFormRequestClearFieldToFocus",
        "name": "ezFormRequestClearFieldToFocus",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Emitido quando o campo recebe foco"
        },
        "complexType": {
          "original": "void",
          "resolved": "void",
          "references": {}
        }
      }, {
        "method": "ezFormSetFields",
        "name": "ezFormSetFields",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Emitido quando o campo recebe foco"
        },
        "complexType": {
          "original": "Array<IFieldConfig>",
          "resolved": "IFieldConfig[]",
          "references": {
            "Array": {
              "location": "global"
            },
            "IFieldConfig": {
              "location": "import",
              "path": "../../utils/form/interfaces"
            }
          }
        }
      }, {
        "method": "ezReady",
        "name": "ezReady",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Evento disparado quando o formul\u00E1rio est\u00E1 dispon\u00EDvel na DOM."
        },
        "complexType": {
          "original": "void",
          "resolved": "void",
          "references": {}
        }
      }, {
        "method": "formItemsReady",
        "name": "formItemsReady",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [],
          "text": "Respons\u00E1vel por notificar quando ocorrer a renderiza\u00E7\u00E3o de itens do formul\u00E1rio."
        },
        "complexType": {
          "original": "FormItems",
          "resolved": "FormItems",
          "references": {
            "FormItems": {
              "location": "import",
              "path": "../ez-form-view/structure"
            }
          }
        }
      }];
  }
  static get methods() {
    return {
      "validate": {
        "complexType": {
          "signature": "() => Promise<void>",
          "parameters": [],
          "references": {
            "Promise": {
              "location": "global"
            }
          },
          "return": "Promise<void>"
        },
        "docs": {
          "text": "Realiza valida\u00E7\u00E3o no conte\u00FAdo de todos os campos.",
          "tags": []
        }
      },
      "addCustomEditor": {
        "complexType": {
          "signature": "(fieldName: string, customEditor: ICustomEditor, detailContext?: string) => Promise<void>",
          "parameters": [{
              "tags": [],
              "text": ""
            }, {
              "tags": [],
              "text": ""
            }, {
              "tags": [],
              "text": ""
            }],
          "references": {
            "Promise": {
              "location": "global"
            },
            "ICustomEditor": {
              "location": "import",
              "path": "../../utils/customEditor/interfaces/ICustomEditor"
            }
          },
          "return": "Promise<void>"
        },
        "docs": {
          "text": "Registra um editor customizado para campos da grade e formul\u00E1rio.",
          "tags": []
        }
      },
      "setFieldProp": {
        "complexType": {
          "signature": "(fieldName: string, propName: string, value: any) => Promise<void>",
          "parameters": [{
              "tags": [],
              "text": ""
            }, {
              "tags": [],
              "text": ""
            }, {
              "tags": [],
              "text": ""
            }],
          "references": {
            "Promise": {
              "location": "global"
            }
          },
          "return": "Promise<void>"
        },
        "docs": {
          "text": "Altera/adiciona uma propriedade nos metadados do campo.",
          "tags": []
        }
      }
    };
  }
  static get elementRef() { return "_element"; }
  static get watchers() {
    return [{
        "propName": "config",
        "methodName": "observeConfig"
      }];
  }
}
