import { ApplicationContext, ElementIDUtils, ObjectUtils, StringUtils } from '@sankhyalabs/core';
import { h, Host } from '@stencil/core';
import { buildGridMetadata, PESQUISA_VIEW_MODE } from './pesquisaHelper';
import { PesquisaFetcher } from '../../lib';
export class SnkPesquisa {
  constructor() {
    this._mdByName = {};
    this._startHighlightTag = "<span class='card-item__highlight'>";
    this._endHighlightTag = "</span>";
    this._specialCharsRegex = /[[.\-\$\+\*,_\&\(\)%\/\\#@!:\|\=\'\"]/gmi;
    this._charsWithAccentuation = "ÁÉÍÓÚ_ÃÕ_ÂÊÎÔÛ_ÀÈÌÒÙ_Ü_Ç_áéíóú_ãõ_âêîôû_àèìòù_ü_ç";
    this._charsWithoutAccentuation = "AEIOU_AO_AEIOU_AEIOU_U_C_aeiou_ao_aeiou_aeiou_u_c";
    this._changeDeboucingTimeout = null;
    this._limitCharsToSearch = 3;
    this._deboucingTime = 450;
    this._gridDataSource = [];
    this.pesquisaFetcher = new PesquisaFetcher();
    this._itemList = undefined;
    this._startLoading = false;
    this._presentationMode = "list";
    this._currentView = undefined;
    this._valideDataSource = false;
    this.searchLoader = undefined;
    this.treeLoader = undefined;
    this.selectItem = undefined;
    this.entityName = undefined;
    this.argument = undefined;
    this.isHierarchyEntity = false;
    this.allowsNonAnalytic = false;
  }
  observeArgument() {
    if (this._textInput) {
      this._textInput.value = this.argument;
    }
  }
  clearSearch() {
    this.argument = "";
    this._itemList = undefined;
  }
  buildDetails(fields, item) {
    let details = {};
    for (let index = 0; (index < fields.length); index++) {
      const field = fields[index];
      const itemValue = item[field.fieldName];
      const optionValue = field.options ? field.options[itemValue] || itemValue : itemValue;
      details[field.description] = this.highlightValue(item['__matchFields'], optionValue, fields, true);
    }
    return details;
  }
  buildFirstFields(matchFields, fields, qtyFields = 6) {
    if (matchFields && Array.isArray(matchFields)) {
      matchFields.forEach((matchField) => {
        let fieldMD = this.removeReference(fields, this._mdByName[matchField]);
        if (fieldMD) {
          fields.unshift(fieldMD);
        }
      });
    }
    fields = fields.slice(0, qtyFields);
    return fields;
  }
  doSearch(argument) {
    if (this.searchLoader && typeof this.searchLoader === "function") {
      this._startLoading = true;
      this.searchLoader(argument)
        .then((resp) => {
        if (!resp)
          return;
        this.processSearchResponse(resp);
      }).finally(() => {
        this._startLoading = false;
      });
    }
  }
  processSearchResponse(resp) {
    resp = ObjectUtils.stringToObject(resp.json.$);
    let descriptionField = resp.descriptionField;
    let pkField = resp.pkField;
    if (!pkField)
      return;
    this._gridMetadata = buildGridMetadata(resp);
    this._gridDataSource = [...resp.data];
    this._pkField = pkField;
    this._descriptionField = descriptionField;
    this._itemList = this.buildCardItemList(resp, pkField, descriptionField);
  }
  buildCardItemList(resp, pkField, descriptionField) {
    var _a;
    let newItemList = (_a = resp.data) === null || _a === void 0 ? void 0 : _a.map((item) => {
      var _a;
      let fieldsMetadata = this.filterFieldsMetadata(resp, item);
      let fields = this.buildFirstFields(item['__matchFields'], fieldsMetadata);
      return {
        key: this.highlightValue(item['__matchFields'], (_a = item[pkField]) === null || _a === void 0 ? void 0 : _a.toString(), fields, true),
        title: descriptionField ? this.highlightValue(item['__matchFields'], item[descriptionField], fields, true) : '',
        details: this.buildDetails(fields, item),
      };
    });
    return newItemList;
  }
  filterFieldsMetadata(resp, item) {
    return resp.fieldsMetadata.filter((field) => {
      let accept = !StringUtils.isEmpty(item[field.fieldName]) &&
        field.visible !== false &&
        field.type !== 'B' &&
        resp.pkField !== field.fieldName &&
        resp.descriptionField !== field.fieldName &&
        (field.isPrimaryKey || !field.isLinkField) &&
        !(field.type === 'S' && field.presentationType === 'H');
      if (accept) {
        this._mdByName[field.fieldName] = field;
      }
      //Condição que trata o problema de trazer imagem no entityCard na pesquisa de registros
      if (typeof item[field.fieldName] === 'string' && (item[field.fieldName].indexOf('<img') > -1 || item[field.fieldName].indexOf('<svg') > -1)) {
        return false;
      }
      return accept;
    });
  }
  removeAtIndex(array, index) {
    if (index >= 0 && index < array.length) {
      return array.splice(index, 1)[0];
    }
  }
  removeReference(array, obj) {
    let index = this.indexOf(array, obj);
    return this.removeAtIndex(array, index);
  }
  indexOf(arr, obj) {
    let index = -1;
    if (Array.isArray(arr)) {
      this.find(arr, (item, i) => {
        index = i;
        return this.equals(obj, item);
      });
    }
    return index;
  }
  equals(obj1, obj2) {
    return ObjectUtils.objectToString(obj1) === ObjectUtils.objectToString(obj2);
  }
  find(arr, checkerFn) {
    if (arr) {
      for (let i = 0, length = arr.length; i < length; i++) {
        if (checkerFn(arr[i], i, arr)) {
          return arr[i];
        }
      }
    }
    else {
      return undefined;
    }
  }
  /**
    matchFields-> array __matchFields do data
    value -> termo que está sendo pesquisao
    fieldMD -> metadados retornados pelo serviço de pesquisa
    forceMatch -> força a pesquisa mesmo que não tenha __matchFields no metadado
  */
  highlightValue(matchFields, value, fieldMD, forceMatch) {
    let valueAux = this.replaceHtmlEntities(value);
    if (this.argument && (forceMatch || this.isIn(matchFields, fieldMD.fieldName))) {
      const argumentNumber = this.getArgumentNumber();
      let replacements = (isNaN(argumentNumber) ? this.argument : argumentNumber.toString()).split(/%|,|\s+/);
      let lastMatchIndex = 0;
      let cleanText = this.replaceAccentuatedChars(valueAux);
      replacements.forEach((r) => {
        if (r) {
          r = this.replaceAccentuatedChars(r);
          let specialChars = this.getSpecialCharacters(r);
          if (specialChars != undefined && specialChars.length > 0) {
            r = this.removeSpecialCharacters(r);
          }
          if (fieldMD.mask || fieldMD.uiType === 'CGC_CPF' || fieldMD.uiType === 'Phone') {
            //Fazemos isso para descondirerar os caracteres especiais de formatação da string
            r = r.split('').join('\\.?\\-?\\/?\\(?\\)?');
          }
          let regex = new RegExp(r, "ig");
          regex.lastIndex = lastMatchIndex;
          let match = regex.exec(cleanText);
          if (match && match.length > 0) {
            lastMatchIndex = match.index;
            let sizeMatch = match[0].length;
            let txtToColor = valueAux.substring(lastMatchIndex, lastMatchIndex + sizeMatch);
            let txtColored = (txtToColor === null || txtToColor === void 0 ? void 0 : txtToColor.trim()) ? this._startHighlightTag + txtToColor + this._endHighlightTag : '';
            let start = valueAux.substring(0, lastMatchIndex);
            let end = valueAux.substring(lastMatchIndex + sizeMatch);
            valueAux = start + txtColored + end;
            lastMatchIndex = lastMatchIndex + txtColored.length;
            cleanText = this.replaceAccentuatedChars(valueAux);
          }
        }
      });
    }
    return valueAux;
  }
  replaceHtmlEntities(source) {
    if (source == undefined) {
      return source;
    }
    return String(source)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;');
  }
  replaceAccentuatedChars(source) {
    if (source == undefined) {
      return source;
    }
    const replaceList = ['&amp;', '&lt;', '&gt;', '&quot;'];
    const sourceAux = this.replaceToSpace(source, replaceList);
    return sourceAux.replace(/[^\w ]/g, (char) => {
      let index = this._charsWithAccentuation.indexOf(char);
      if (index > -1) {
        char = this._charsWithoutAccentuation.charAt(index);
      }
      return char || '';
    });
  }
  replaceToSpace(source, replaceList = []) {
    if (source == undefined) {
      return source;
    }
    replaceList.forEach((word) => {
      const wordAux = new RegExp(word, 'g');
      let whiteSpace = '';
      for (let i = 0; i < word.length; i++) {
        whiteSpace += ' ';
      }
      source = String(source).replace(wordAux, whiteSpace);
    });
    return source;
  }
  isIn(arr, obj) {
    return this.indexOf(arr, obj) > -1;
  }
  getSpecialCharacters(str) {
    let match;
    let charList = [];
    while ((match = this._specialCharsRegex.exec(str))) {
      charList.push(str[match.index]);
    }
    return charList;
  }
  removeSpecialCharacters(str) {
    let specialChars = this.getSpecialCharacters(str);
    specialChars.forEach((ch) => {
      str = this.replaceAll(str, ch, '');
    });
    return str;
  }
  replaceAll(str, strFrom, strTo) {
    let pos = (str != undefined ? str : '').indexOf(strFrom);
    while (pos > -1) {
      str = str.replace(strFrom, strTo);
      pos = str.indexOf(strFrom);
    }
    return (str);
  }
  getArgumentNumber() {
    return Number(this.argument || undefined);
  }
  createOption(detail) {
    let { key, title } = detail;
    const startHighlight = new RegExp(this._startHighlightTag, 'g');
    const endHighlight = new RegExp(this._endHighlightTag, 'g');
    title = StringUtils.decodeHtmlEntities(title);
    const option = {
      value: key === null || key === void 0 ? void 0 : key.replace(startHighlight, '').replace(endHighlight, ''),
      label: title === null || title === void 0 ? void 0 : title.replace(startHighlight, '').replace(endHighlight, '')
    };
    this.selectItem(option);
  }
  onChangeValue(event) {
    var _a;
    this.clearDeboucingTimeout();
    if (this._startLoading) {
      this._changeDeboucingTimeout = setTimeout(() => {
        this.onChangeValue(event);
      }, this._deboucingTime);
      return;
    }
    this.argument = (event.detail || "").trim();
    if (this.isHierarchyEntity && this._presentationMode === 'list') {
      (_a = this._pesquisaTree) === null || _a === void 0 ? void 0 : _a.applyFilter();
      return;
    }
    const argumentNumber = this.getArgumentNumber();
    if (this.argument && (!isNaN(argumentNumber) || this.argument.length >= this._limitCharsToSearch)) {
      this._changeDeboucingTimeout = setTimeout(() => {
        this.doSearch(this.argument);
        this._valideDataSource = true;
      }, this._deboucingTime);
    }
    else {
      this._itemList = undefined;
      this._gridDataSource = [];
      this._valideDataSource = false;
    }
  }
  clearDeboucingTimeout() {
    if (this._changeDeboucingTimeout) {
      clearTimeout(this._changeDeboucingTimeout);
      this._changeDeboucingTimeout = undefined;
    }
  }
  onClickSearch() {
    this.doSearch((this.argument || "").trim());
    this._valideDataSource = true;
  }
  getMessageView() {
    if (this._startLoading)
      return this.getMessage('snkPesquisa.search.waitMessage');
    if (!this._itemList)
      return this.getMessage('snkPesquisa.search.initialMessage');
    const itemsCount = this._itemList.length;
    if (itemsCount < 1)
      return this.getMessage('snkPesquisa.search.noResultsMessage');
    if (itemsCount === 1)
      return this.getMessage('snkPesquisa.search.resultsFoundSingular');
    return this.getMessage('snkPesquisa.search.resultsFoundPlural', { COUNT: itemsCount });
  }
  async componentWillLoad() {
    var _a;
    this._application = ApplicationContext.getContextValue("__SNK__APPLICATION__");
    this._messagesBuilder = (_a = this._application) === null || _a === void 0 ? void 0 : _a.messagesBuilder;
    const mode = await this.pesquisaFetcher.loadPresentationConfig(this.entityName);
    this._presentationMode = mode === 'list' ? 'list' : 'table';
  }
  async componentDidLoad() {
    ElementIDUtils.addIDInfo(this._element);
    await this.chooseViewMode();
  }
  getMessage(key, params) {
    return this._messagesBuilder.getMessage(key, params);
  }
  async handleChangeViewMode() {
    this._presentationMode = this._presentationMode === 'list' ? 'table' : 'list';
    await this.chooseViewMode();
    await this.pesquisaFetcher.savePresentationConfig(this.entityName, this._presentationMode);
  }
  async chooseViewMode() {
    var _a, _b;
    if (this._presentationMode !== 'list') {
      await ((_a = this._viewStack) === null || _a === void 0 ? void 0 : _a.show(PESQUISA_VIEW_MODE.GRID));
      this._currentView = PESQUISA_VIEW_MODE.GRID;
      return;
    }
    const view = this.isHierarchyEntity ? PESQUISA_VIEW_MODE.TREE : PESQUISA_VIEW_MODE.CARDS;
    await ((_b = this._viewStack) === null || _b === void 0 ? void 0 : _b.show(view));
    this._currentView = view;
  }
  //Os limites de exibição são diferentes para lista e grade
  hideLimitMessage() {
    if (!this._itemList)
      return true;
    return this._itemList.length < 100;
  }
  hideRecordsMessage() {
    return (this._presentationMode === "list" && this.isHierarchyEntity);
  }
  getButtonModeInfo() {
    if (this._presentationMode === 'list') {
      return {
        iconName: 'table',
        title: this.getMessage('snkPesquisa.viewMode.tableMode'),
      };
    }
    if (this.isHierarchyEntity) {
      return {
        iconName: 'hierarchical-tree',
        title: this.getMessage('snkPesquisa.viewMode.tableMode'),
      };
    }
    return {
      iconName: 'list',
      title: this.getMessage('snkPesquisa.viewMode.listMode'),
    };
  }
  render() {
    var _a, _b;
    return (h(Host, null, h("div", { class: "snk-pesquisa" }, h("div", { class: "snk-pesquisa__input" }, h("ez-text-input", { label: this.getMessage('snkPesquisa.search.label'), class: "ez-margin-right--medium", canShowError: false, ref: (ref) => this._textInput = ref, onEzChange: (event) => this.onChangeValue(event), value: this.argument, "data-element-id": "searchInput" }, h("ez-icon", { slot: "leftIcon", iconName: "search" }), this.argument && ((_a = this._textInput) === null || _a === void 0 ? void 0 : _a.value)
      ? h("button", { slot: "rightIcon", class: "snk-pesquisa__btn", onClick: () => this.clearSearch() }, h("ez-icon", { iconName: "close" }))
      : undefined), h("ez-button", { class: "ez-button--primary", label: this.getMessage('snkPesquisa.search.button'), onClick: () => this.onClickSearch() }), h("ez-button", { class: "ez-margin-left--medium", mode: "icon", iconName: this.getButtonModeInfo().iconName, title: this.getButtonModeInfo().title, onClick: async () => await this.handleChangeViewMode() })), h("div", { class: `snk-pesquisa__records-label ${this._currentView === PESQUISA_VIEW_MODE.TREE ? 'hidden' : ''}` }, h("label", { class: `snk-pesquisa__records ${this.hideRecordsMessage() ? "hidden" : ""}` }, this.getMessageView()), h("label", { class: `snk-pesquisa__records-limit ${this.hideLimitMessage() ? "hidden" : ""}` }, this.getMessage('snkPesquisa.search.limitMessage'))), h("div", { class: "snk-pesquisa__content" }, h("ez-view-stack", { ref: (ref) => this._viewStack = ref, "data-element-id": "simple-crud" }, h("stack-item", null, this._valideDataSource &&
      h("pesquisa-grid", { metadata: this._gridMetadata, dataSource: this._gridDataSource, pkField: this._pkField, descriptionField: this._descriptionField, entityName: this.entityName, shouldLoadConfig: this._currentView === PESQUISA_VIEW_MODE.GRID, onPesquisaGridSelectItem: ({ detail }) => this.selectItem(detail) })), h("stack-item", null, h("div", { class: 'snk-pesquisa__cards-container' }, (_b = this._itemList) === null || _b === void 0 ? void 0 : _b.map((item) => {
      return (h("ez-card-item", { onEzClick: (event) => this.createOption(event.detail), item: item }));
    }))), h("stack-item", null, h("pesquisa-tree", { ref: (element) => this._pesquisaTree = element, argument: this.argument, treeLoader: this.treeLoader, allowsNonAnalytic: this.allowsNonAnalytic, shouldLoadTree: this._currentView === PESQUISA_VIEW_MODE.TREE, onPesquisaTreeSelectItem: ({ detail }) => this.selectItem(detail), messagesBuilder: this._messagesBuilder })))))));
  }
  static get is() { return "snk-pesquisa"; }
  static get encapsulation() { return "scoped"; }
  static get originalStyleUrls() {
    return {
      "$": ["snk-pesquisa.css"]
    };
  }
  static get styleUrls() {
    return {
      "$": ["snk-pesquisa.css"]
    };
  }
  static get properties() {
    return {
      "searchLoader": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "(text: string) => Promise<any>",
          "resolved": "(text: string) => Promise<any>",
          "references": {
            "Promise": {
              "location": "global"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{(text: string) => Promise<any>}"
            }, {
              "name": "param",
              "text": "text Texto utilizado como argumento de pesquisa."
            }, {
              "name": "returns",
              "text": "Promise com os dados da pesquisa."
            }, {
              "name": "example",
              "text": "searchLoader={(text) => fetchItems(text)}"
            }],
          "text": "Fun\u00E7\u00E3o respons\u00E1vel em carregar os itens do componente snk-pesquisa.\nDeve retornar uma Promise com os dados encontrados."
        }
      },
      "treeLoader": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "(text: string) => Promise<any>",
          "resolved": "(text: string) => Promise<any>",
          "references": {
            "Promise": {
              "location": "global"
            }
          }
        },
        "required": false,
        "optional": true,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{(text: string) => Promise<any>}"
            }, {
              "name": "param",
              "text": "text Texto utilizado como argumento de pesquisa para a \u00E1rvore."
            }, {
              "name": "returns",
              "text": "Promise com os dados da \u00E1rvore."
            }],
          "text": "Fun\u00E7\u00E3o respons\u00E1vel por carregar a \u00E1rvore hier\u00E1rquica do componente.\nOpcional. Caso n\u00E3o seja fornecida, o modo \u00E1rvore n\u00E3o estar\u00E1 dispon\u00EDvel."
        }
      },
      "selectItem": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "(option: IOption) => void",
          "resolved": "(option: IOption) => void",
          "references": {
            "IOption": {
              "location": "import",
              "path": "@sankhyalabs/ezui/dist/types/components/ez-combo-box/ez-combo-box"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{(option: IOption) => void}"
            }, {
              "name": "param",
              "text": "option Item selecionado."
            }, {
              "name": "returns",
              "text": "void"
            }, {
              "name": "example",
              "text": "selectItem={(option) => handleSelect(option)}"
            }],
          "text": "Fun\u00E7\u00E3o disparada ao selecionar um item da pesquisa."
        }
      },
      "entityName": {
        "type": "string",
        "mutable": false,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{string}"
            }, {
              "name": "example",
              "text": "entityName=\"Produto\""
            }],
          "text": "Nome da entidade sobre a qual a pesquisa ser\u00E1 realizada."
        },
        "attribute": "entity-name",
        "reflect": false
      },
      "argument": {
        "type": "string",
        "mutable": true,
        "complexType": {
          "original": "string",
          "resolved": "string",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{string}"
            }, {
              "name": "mutable",
              "text": undefined
            }, {
              "name": "example",
              "text": "argument=\"Texto para pesquisa\""
            }],
          "text": "Argumento utilizado ao chamar a fun\u00E7\u00E3o searchLoader.\nPode ser alterado externamente."
        },
        "attribute": "argument",
        "reflect": false
      },
      "isHierarchyEntity": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{boolean}"
            }, {
              "name": "default",
              "text": "false"
            }, {
              "name": "example",
              "text": "isHierarchyEntity={true}"
            }],
          "text": "Define se a popup de pesquisa ter\u00E1 ou n\u00E3o modo hier\u00E1rquico."
        },
        "attribute": "is-hierarchy-entity",
        "reflect": false,
        "defaultValue": "false"
      },
      "allowsNonAnalytic": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{boolean}"
            }, {
              "name": "default",
              "text": "false"
            }, {
              "name": "example",
              "text": "allowsNonAnalytic={true}"
            }],
          "text": "Define se permite a sele\u00E7\u00E3o de itens n\u00E3o anal\u00EDticos."
        },
        "attribute": "allows-non-analytic",
        "reflect": false,
        "defaultValue": "false"
      }
    };
  }
  static get states() {
    return {
      "_itemList": {},
      "_startLoading": {},
      "_presentationMode": {},
      "_currentView": {},
      "_valideDataSource": {}
    };
  }
  static get elementRef() { return "_element"; }
  static get watchers() {
    return [{
        "propName": "argument",
        "methodName": "observeArgument"
      }];
  }
}
