import { h } from "@stencil/core";
import { SelectedItemType } from "./interfaces";
import { ApplicationContext, ElementIDUtils, JSUtils } from "@sankhyalabs/core";
import { ListItem } from "./subcomponents";
const SNK_CONTEXT_KEY = "__SNK__APPLICATION__";
const ENTITY_NAME_PATTERN = /\w+:\/\/(\w+)/;
export class SnkFilterFieldSearch {
  constructor() {
    this._currentMetadata = null;
    this._filterText = "";
    this._isLoading = false;
    this.searchable = true;
    this.fieldsDataSource = undefined;
    this.breadcrumbItems = [];
    this.linkItems = [];
    this.fieldItems = [];
    this.searchEmpty = false;
    this.groupEmpty = false;
  }
  /**
   * /**
   * Realiza a abertura do componente abaixo do elemento HTML informado
   * e faz a primeira carga de dados.
   *
   * @param {HTMLElement} element - Elemento HTML onde o snk-filter-field-search irá se posicionar abaixo.
   * @param {IEzPopoverAnchorOptions} options - opções para regular o posicionamento do componente.
   */
  async show(element, options) {
    var _a;
    if (element) {
      (_a = this._ezPopover) === null || _a === void 0 ? void 0 : _a.showUnder(element, options);
    }
    else {
      this._ezPopover.show();
    }
    return this.loadData();
  }
  /**
   * Filtra a fonte de dados do componente.
   * @param {string} filterText - Texto para filtrar a fonte de dados.
   * @throws {Error} - Quando o componente estiver com a propriedade searchable como true.
  */
  async applyFilter(filterText) {
    if (this.searchable)
      throw new Error('This method is not available when searchable is true');
    this.handleFilterChange(filterText);
  }
  async loadData() {
    var _a, _b;
    this._isLoading = true;
    let metadata = await this.fieldsDataSource.fetchData(this._currentLink);
    this._currentLink = metadata.currentLink;
    const { fields, links } = metadata || {};
    if (!Array.isArray(fields) || !Array.isArray(links))
      throw new Error('Invalid metadata');
    this._currentMetadata = metadata;
    this.mapDataSourceToLinkItems();
    this.mapDataSourceToFieldItems();
    this._isLoading = false;
    (_a = this._ezFieldList) === null || _a === void 0 ? void 0 : _a.scrollToTop();
    (_b = this._ezLinkList) === null || _b === void 0 ? void 0 : _b.scrollToTop();
  }
  mapDataSourceToLinkItems() {
    var _a;
    const { links } = this._currentMetadata;
    const filterLinkLabelMessage = this.getMessage('snkFilterFieldSearch.linkLabel', { link: (_a = this._currentLink) === null || _a === void 0 ? void 0 : _a.description });
    const items = links.map(link => (Object.assign({ label: link.description }, link)));
    this.groupEmpty = Boolean(items.length === 0);
    this.linkItems = [{
        group: filterLinkLabelMessage,
        items,
      }];
    if (this.breadcrumbItems.length === 0) {
      this.breadcrumbItems = [Object.assign({ id: JSUtils.generateUUID(), label: this._currentLink.description }, this._currentLink)];
    }
  }
  mapDataSourceToFieldItems() {
    var _a;
    const fields = this._currentMetadata.fields;
    const filterFieldLabelMessage = this.getMessage('snkFilterFieldSearch.fieldLabel', { link: (_a = this._currentLink) === null || _a === void 0 ? void 0 : _a.description });
    const filteredFields = fields.filter(field => {
      if (!this._currentLink)
        return true;
      const searchableByDescription = field.description.toLowerCase().includes(this._filterText.toLowerCase());
      const searchableByName = field.name.toLowerCase().includes(this._filterText.toLowerCase());
      return searchableByDescription || searchableByName;
    });
    const items = filteredFields.map(field => (Object.assign({ label: field.description }, field)));
    this.searchEmpty = Boolean(this._filterText && items.length === 0);
    this.fieldItems = [{
        group: filterFieldLabelMessage,
        items
      }];
  }
  handleFilterChange(filterText) {
    if (this._isLoading)
      return;
    this._filterText = filterText;
    this.mapDataSourceToFieldItems();
  }
  handleSelectLink({ detail: item }) {
    if (this._isLoading)
      return;
    const breadcrumbByIndex = this.breadcrumbItems.findIndex(breadcrumbItem => breadcrumbItem.id === item.id);
    if (breadcrumbByIndex > -1) {
      this.breadcrumbItems = this.breadcrumbItems.slice(0, (breadcrumbByIndex + 1));
    }
    else {
      this.breadcrumbItems = [...this.breadcrumbItems, Object.assign({ id: JSUtils.generateUUID(), label: item.description }, item)];
    }
    const selectedItem = Object.assign(Object.assign({}, item), { type: SelectedItemType.LINK });
    this._currentLink = selectedItem;
    this.ezSelectFilterItem.emit(selectedItem);
    this.loadData();
  }
  getEntityFromBreadCrumbItem(item) {
    const result = ENTITY_NAME_PATTERN.exec(item.uri);
    if (!result) {
      return undefined;
    }
    return result[1];
  }
  handleSelectField({ detail: item }) {
    var _a;
    const breadcrumbPath = this.breadcrumbItems.map(breadcrumbItem => breadcrumbItem.label).join('>>');
    const selectedField = Object.assign(Object.assign({}, item), { type: SelectedItemType.FIELD, entityPath: this.breadcrumbItems.map(item => this.getEntityFromBreadCrumbItem(item)).filter(item => item != undefined), path: `${breadcrumbPath} >> ${item.description}` });
    this.ezSelectFilterItem.emit(selectedField);
    (_a = this._ezPopover) === null || _a === void 0 ? void 0 : _a.hide();
  }
  getElementID(sufix) {
    return {
      [ElementIDUtils.DATA_ELEMENT_ID_ATTRIBUTE_NAME]: ElementIDUtils.getInternalIDInfo(sufix)
    };
  }
  /**
   * 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-filter-field-search.msg.ts"
  */
  getMessage(key, params) {
    var _a, _b, _c;
    if (!this._application)
      this._application = ApplicationContext.getContextValue(SNK_CONTEXT_KEY);
    return (_c = (_b = (_a = this._application) === null || _a === void 0 ? void 0 : _a.messagesBuilder) === null || _b === void 0 ? void 0 : _b.getMessage) === null || _c === void 0 ? void 0 : _c.call(_b, key, params);
  }
  ;
  componentWillLoad() {
    this._application = ApplicationContext.getContextValue(SNK_CONTEXT_KEY);
  }
  componentDidLoad() {
    ElementIDUtils.addIDInfoIfNotExists(this._ezPopover, 'popover');
  }
  disconnectedCallback() {
    var _a;
    (_a = this._ezPopover) === null || _a === void 0 ? void 0 : _a.hide();
  }
  render() {
    var _a;
    const searchLabelMessage = this.getMessage('snkFilterFieldSearch.searchLabel');
    return (h("ez-popover", Object.assign({ ref: (el) => this._ezPopover = el, role: "dialog", overlayType: "none", "aria-hidden": !((_a = this._ezPopover) === null || _a === void 0 ? void 0 : _a.opened) }, this.getElementID('EzPopover')), h("div", { class: "snk-filter-field-search__container" }, h("header", { class: "snk-filter-field-search__header" }, this.searchable && !!searchLabelMessage && (h("ez-filter-input", Object.assign({ class: "snk-filter-field-search__input", label: searchLabelMessage, asyncSearch: true, canShowError: false, onEzSearching: (event) => this.handleFilterChange.bind(this)(event.detail), "aria-label": searchLabelMessage, "aria-required": "false", "aria-invalid": "false" }, this.getElementID('EzFilterInput')))), this.breadcrumbItems.length > 0 && (h("ez-breadcrumb", Object.assign({ items: this.breadcrumbItems, onSelectedItem: this.handleSelectLink.bind(this), role: "navigation", "aria-current": "step" }, this.getElementID('EzBreadcrumb')))), h("hr", { class: "ez-divider-horizontal ez-margin-vertical--medium" })), h("section", { class: `snk-filter-field-search__section ${(this.groupEmpty && this.searchEmpty) && " ez-margin--auto"}` }, this.groupEmpty ?
      h("div", { class: "ez-margin--auto" }, h("span", { class: "ez-text ez-text--secondary ez-text--medium" }, this.getMessage('snkFilterFieldSearch.groupEmpty')))
      :
        h("ez-list", Object.assign({ ref: (el) => this._ezLinkList = el, class: "snk-filter-field-search__list", id: "filterLinkList", useGroups: true, dataSource: this.linkItems, onEzSelectItem: this.handleSelectLink.bind(this), hoverFeedback: true, ezSelectable: true, itemSlotBuilder: listItem => h("ez-badge", { label: listItem.fieldCount }), "aria-describedby": "filterLinkLabel" }, this.getElementID('EzListLinks'))), this.searchEmpty ?
      h("div", { class: "ez-margin--auto" }, h("span", { class: "ez-text ez-text--secondary ez-text--medium" }, this.getMessage('snkFilterFieldSearch.searchEmpty')))
      :
        h("ez-list", Object.assign({ ref: (el) => this._ezFieldList = el, class: "snk-filter-field-search__list", id: "filterFieldList", useGroups: true, dataSource: this.fieldItems, onEzSelectItem: this.handleSelectField.bind(this), hoverFeedback: true, ezSelectable: true, itemSlotBuilder: ListItem, "aria-describedby": "filterFieldLabel" }, this.getElementID('EzListFields')))))));
  }
  static get is() { return "snk-filter-field-search"; }
  static get encapsulation() { return "scoped"; }
  static get originalStyleUrls() {
    return {
      "$": ["snk-filter-field-search.css"]
    };
  }
  static get styleUrls() {
    return {
      "$": ["snk-filter-field-search.css"]
    };
  }
  static get properties() {
    return {
      "searchable": {
        "type": "boolean",
        "mutable": false,
        "complexType": {
          "original": "boolean",
          "resolved": "boolean",
          "references": {}
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Define se o componente ir\u00E1 possuir um campo de pesquisa."
        },
        "attribute": "searchable",
        "reflect": false,
        "defaultValue": "true"
      },
      "fieldsDataSource": {
        "type": "unknown",
        "mutable": false,
        "complexType": {
          "original": "FilterFieldsDataSource",
          "resolved": "FilterFieldsDataSource",
          "references": {
            "FilterFieldsDataSource": {
              "location": "import",
              "path": "./interfaces"
            }
          }
        },
        "required": false,
        "optional": false,
        "docs": {
          "tags": [],
          "text": "Define a fonte de dados que o componente vai utilizar\npara carregamento dos campos."
        }
      }
    };
  }
  static get states() {
    return {
      "breadcrumbItems": {},
      "linkItems": {},
      "fieldItems": {},
      "searchEmpty": {},
      "groupEmpty": {}
    };
  }
  static get events() {
    return [{
        "method": "ezSelectFilterItem",
        "name": "ezSelectFilterItem",
        "bubbles": true,
        "cancelable": true,
        "composed": true,
        "docs": {
          "tags": [{
              "name": "type",
              "text": "{EventEmitter<IFilterField | IFilterLink>}"
            }],
          "text": "Emitido sempre que um item da lista for selecionado"
        },
        "complexType": {
          "original": "IFilterField | IFilterLink",
          "resolved": "IFilterField | IFilterLink",
          "references": {
            "IFilterField": {
              "location": "import",
              "path": "./interfaces"
            },
            "IFilterLink": {
              "location": "import",
              "path": "./interfaces"
            }
          }
        }
      }];
  }
  static get methods() {
    return {
      "show": {
        "complexType": {
          "signature": "(element?: HTMLElement, options?: IEzPopoverAnchorOptions) => Promise<void>",
          "parameters": [{
              "tags": [{
                  "name": "param",
                  "text": "element - Elemento HTML onde o snk-filter-field-search ir\u00E1 se posicionar abaixo."
                }],
              "text": "- Elemento HTML onde o snk-filter-field-search ir\u00E1 se posicionar abaixo."
            }, {
              "tags": [{
                  "name": "param",
                  "text": "options - op\u00E7\u00F5es para regular o posicionamento do componente."
                }],
              "text": "- op\u00E7\u00F5es para regular o posicionamento do componente."
            }],
          "references": {
            "Promise": {
              "location": "global"
            },
            "HTMLElement": {
              "location": "global"
            },
            "IEzPopoverAnchorOptions": {
              "location": "import",
              "path": "@sankhyalabs/ezui/dist/types/components/ez-popover/ez-popover"
            }
          },
          "return": "Promise<void>"
        },
        "docs": {
          "text": "/**\nRealiza a abertura do componente abaixo do elemento HTML informado\ne faz a primeira carga de dados.",
          "tags": [{
              "name": "param",
              "text": "element - Elemento HTML onde o snk-filter-field-search ir\u00E1 se posicionar abaixo."
            }, {
              "name": "param",
              "text": "options - op\u00E7\u00F5es para regular o posicionamento do componente."
            }]
        }
      },
      "applyFilter": {
        "complexType": {
          "signature": "(filterText: string) => Promise<void>",
          "parameters": [{
              "tags": [{
                  "name": "param",
                  "text": "filterText - Texto para filtrar a fonte de dados."
                }],
              "text": "- Texto para filtrar a fonte de dados."
            }],
          "references": {
            "Promise": {
              "location": "global"
            }
          },
          "return": "Promise<void>"
        },
        "docs": {
          "text": "Filtra a fonte de dados do componente.",
          "tags": [{
              "name": "param",
              "text": "filterText - Texto para filtrar a fonte de dados."
            }, {
              "name": "throws",
              "text": "{Error} - Quando o componente estiver com a propriedade searchable como true."
            }]
        }
      }
    };
  }
}
