import { Component, Prop, h, State, Watch, Event, Method, Element, Listen } from '@stencil/core';
import Fuse from 'fuse.js';
import { FormSubmitController } from '../../../functions/form-data';
import { __ } from '@wordpress/i18n';
import { isValidURL } from '../../../functions/util';
let id = 0;
let itemIndex = 0;
let arrowFlag = '';
/**
 * @part base - The elements base wrapper.
 * @part input - The html input element.
 * @part form-control - The form control wrapper.
 * @part label - The input label.
 * @part help-text - Help text that describes how to use the input.
 * @part trigger - The trigger for the dropdown.
 * @part panel - Them panel for the dropdown.
 * @part caret - The caret.
 * @part search___base - The search base wrapper.
 * @part search__input - The search input element.
 * @part search__form-control - The search form control wrapper.
 * @part menu__base - The menu base.
 * @part spinner__base - The spinner base.
 * @part empty - The empty message.
 */
export class ScSelectDropdown {
  constructor() {
    this.inputId = `select-${++id}`;
    this.helpId = `select-help-text-${id}`;
    this.labelId = `select-label-${id}`;
    /** Placeholder for no value */
    this.placeholder = '';
    /** Placeholder for search */
    this.searchPlaceholder = '';
    /** The input's value attribute. */
    this.value = '';
    /** The input's value attribute. */
    this.choices = [];
    /** Can we unselect items. */
    this.unselect = true;
    /** The input's size. */
    this.size = 'medium';
    this.position = 'bottom-right';
    /**
     * This will be true when the control is in an invalid state. Validity is determined by props such as `type`,
     * `required`, `minlength`, `maxlength`, and `pattern` using the browser's constraint validation API.
     */
    this.invalid = false;
    this.showParentLabel = true;
    this.hoist = false;
    this.hasFocus = false;
    /** Search term */
    this.searchTerm = '';
    /** Search term */
    this.filteredChoices = [];
  }
  /** Trigger focus on show */
  handleShow() {
    this.open = true;
    this.scOpen.emit();
    setTimeout(() => {
      this.searchInput && this.searchInput.triggerFocus();
    }, 50);
  }
  handleHide() {
    this.open = false;
    itemIndex = 0;
    this.scClose.emit();
  }
  handleBlur() {
    this.hasFocus = false;
    this.scBlur.emit();
  }
  handleFocus() {
    this.hasFocus = true;
    this.scFocus.emit();
  }
  /** Get the display value of the item. */
  displayValue() {
    var _a;
    if (!this.value)
      return false;
    let chosen = this.choices.find(choice => choice.value == this.value);
    let append = '';
    if (!chosen) {
      if (this.showParentLabel) {
        append = (_a = this.choices.find(choice => { var _a, _b; return (_b = (_a = choice === null || choice === void 0 ? void 0 : choice.choices) === null || _a === void 0 ? void 0 : _a.some) === null || _b === void 0 ? void 0 : _b.call(_a, subChoice => subChoice.value === this.value); })) === null || _a === void 0 ? void 0 : _a.label;
      }
      const subchoices = (this.choices || []).map(choice => choice.choices).flat();
      chosen = subchoices.find(choice => (choice === null || choice === void 0 ? void 0 : choice.value) == this.value);
    }
    if (chosen) {
      return `${append ? append + ' - ' : ''}${chosen === null || chosen === void 0 ? void 0 : chosen.label}`;
    }
    return false;
  }
  isChecked({ value }) {
    return this.value === value;
  }
  /** Sets a custom validation message. If `message` is not empty, the field will be considered invalid. */
  async setCustomValidity(message) {
    this.input.setCustomValidity(message);
    this.invalid = !this.input.checkValidity();
  }
  async reportValidity() {
    return this.input.reportValidity();
  }
  handleQuery(e) {
    this.searchTerm = e.target.value;
    this.scSearch.emit(this.searchTerm);
  }
  handleSelect(value) {
    if (this.value === value && this.unselect) {
      this.value = '';
    }
    else {
      this.value = value;
    }
    this.scChange.emit();
  }
  handleSearchChange() {
    const fuse = new Fuse(this.choices, {
      keys: ['value', 'label'],
    });
    if (this.searchTerm) {
      const results = fuse.search(this.searchTerm);
      this.filteredChoices = results.map(result => result.item);
    }
    else {
      this.filteredChoices = this.choices;
    }
  }
  handleValueChange() {
    if (this.input) {
      this.invalid = !this.input.checkValidity();
    }
  }
  handleOpenChange() {
    if (this.open) {
      this.scOpen.emit();
      this.searchInput && this.searchInput.triggerFocus();
    }
    else {
      this.scClose.emit();
    }
  }
  handleMenuScroll(e) {
    const scrollTop = e.target.scrollTop;
    const scrollHeight = e.target.scrollHeight;
    const offsetHeight = e.target.offsetHeight;
    const contentHeight = scrollHeight - offsetHeight;
    if (contentHeight - scrollTop < 5)
      this.scScrollEnd.emit();
  }
  componentWillLoad() {
    this.handleSearchChange();
  }
  componentDidLoad() {
    this.formController = new FormSubmitController(this.el).addFormData();
    if (this.open) {
      this.searchInput && this.searchInput.triggerFocus();
    }
  }
  getItems() {
    return [...this.el.shadowRoot.querySelectorAll('sc-menu-item')];
  }
  handleKeyDown(event) {
    const target = event.target;
    const items = this.getItems();
    // Ignore key presses on tags
    if (target.tagName.toLowerCase() === 'sc-tag') {
      return;
    }
    // Tabbing out of the control closes it
    if (event.key === 'Tab') {
      if (this.open) {
        this.handleHide();
      }
      return;
    }
    // Up/down opens the menu
    if (['ArrowDown', 'ArrowUp'].includes(event.key)) {
      event.preventDefault();
      // Show the menu if it's not already open
      if (!this.open) {
        this.handleShow();
      }
      // Focus on a menu item
      if (event.key === 'ArrowDown') {
        if (arrowFlag == 'up') {
          itemIndex = itemIndex + 2;
        }
        if (itemIndex > items.length - 1) {
          itemIndex = 0;
        }
        items[itemIndex].setFocus();
        arrowFlag = 'down';
        itemIndex++;
        return;
      }
      if (event.key === 'ArrowUp') {
        if (arrowFlag == 'down') {
          itemIndex = itemIndex - 2;
        }
        if (itemIndex < 0) {
          itemIndex = items.length - 1;
        }
        items[itemIndex].setFocus();
        arrowFlag = 'up';
        itemIndex--;
        return;
      }
    }
    // Close select dropdown on Esc/Escape key
    if (event.key === 'Escape') {
      if (this.open) {
        this.input.focus();
        this.handleHide();
      }
      return;
    }
    // Open select dropdown with Enter
    if (event.key === 'Enter') {
      if (this.open) {
        items[itemIndex - 1].click();
        this.handleHide();
        this.input.focus();
      }
      else {
        this.handleShow();
      }
    }
    // don't open the menu when a CTRL/Command key is pressed
    if (event.ctrlKey || event.metaKey) {
      return;
    }
    // All other "printable" keys open the menu and initiate type to select
    // TODO: this is closing out the dropdown during typing events.
    // FIX: It must have focus as well. And it fix the random closing issue.
    if (!this.open && this.hasFocus && event.key.length === 1) {
      this.handleShow();
    }
  }
  disconnectedCallback() {
    var _a;
    (_a = this.formController) === null || _a === void 0 ? void 0 : _a.removeFormData();
  }
  renderIcon(icon) {
    if (isValidURL(icon)) {
      return h("img", { src: icon, alt: "icon", slot: "prefix", class: "choice__icon--image" });
    }
    return h("sc-icon", { name: icon, slot: "prefix", class: "choice__icon" });
  }
  renderItem(choice, index) {
    var _a;
    if ((_a = choice === null || choice === void 0 ? void 0 : choice.choices) === null || _a === void 0 ? void 0 : _a.length) {
      return h("sc-menu-label", { key: index }, choice.label);
    }
    return (h("sc-menu-item", { key: index, checked: this.isChecked(choice), value: choice === null || choice === void 0 ? void 0 : choice.value, onClick: () => !choice.disabled && this.handleSelect(choice.value), disabled: choice.disabled },
      choice.label,
      !!(choice === null || choice === void 0 ? void 0 : choice.suffix) && h("span", { slot: "suffix" }, choice.suffix),
      !!(choice === null || choice === void 0 ? void 0 : choice.icon) && this.renderIcon(choice.icon)));
  }
  render() {
    var _a;
    return (h("div", { part: "base", class: {
        'select': true,
        'select--placeholder': !this.value,
        'select--focused': this.hasFocus,
        'select--is-open': !!this.open,
        'select--disabled': this.disabled,
        'select--has-choices': !!((_a = this === null || this === void 0 ? void 0 : this.choices) === null || _a === void 0 ? void 0 : _a.length),
        'select--squared': this.squared,
        'select--squared-bottom': this.squaredBottom,
        'select--squared-top': this.squaredTop,
        'select--squared-left': this.squaredLeft,
        'select--squared-right': this.squaredRight,
      } },
      h("sc-form-control", { exportparts: "label, help-text, form-control", size: this.size, required: this.required, label: this.label, help: this.help, inputId: this.inputId, helpId: this.helpId, labelId: this.labelId, name: this.name },
        h("input", { class: "select__hidden-input", onBlur: () => this.handleBlur(), onFocus: () => this.handleFocus(), name: this.name, ref: el => (this.input = el), value: this.value, required: this.required, disabled: this.disabled }),
        h("sc-dropdown", { exportparts: "trigger, panel", disabled: this.disabled, open: this.open, position: this.position, hoist: this.hoist, style: { '--panel-width': '100%' }, onScShow: () => this.handleShow(), onScHide: () => this.handleHide() },
          h("div", { class: "trigger", slot: "trigger" },
            h("div", { class: "select__value" }, this.displayValue() || this.placeholder || 'Select...'),
            h("sc-icon", { exportparts: "base:caret", class: "select__caret", name: "chevron-down" })),
          this.search && (h("sc-input", { exportparts: "base:search__base, input:search__input, form-control:search__form-control", placeholder: this.searchPlaceholder || 'Search...', onScInput: e => this.handleQuery(e), class: "search", clearable: true, part: "search", ref: el => (this.searchInput = el) }, this.loading && h("sc-spinner", { exportparts: "base:spinner__base", style: { '--spinner-size': '0.5em' }, slot: "suffix" }))),
          h("sc-menu", { style: { maxHeight: '210px', overflow: 'auto' }, exportparts: "base:menu__base", onScroll: e => this.handleMenuScroll(e) },
            h("slot", { name: "prefix" }),
            (this.filteredChoices || []).map((choice, index) => {
              return [this.renderItem(choice, index), (choice.choices || []).map(choice => this.renderItem(choice, index))];
            }),
            this.loading && (h("div", { class: "loading" },
              h("sc-spinner", { exportparts: "base:spinner__base" }))),
            !this.loading && !this.filteredChoices.length && (h("div", { class: "select__empty", part: "empty" }, __('Nothing Found', 'surecart'))),
            h("slot", { name: "suffix" }))))));
  }
  static get is() { return "sc-select"; }
  static get encapsulation() { return "shadow"; }
  static get originalStyleUrls() { return {
    "$": ["sc-select.scss"]
  }; }
  static get styleUrls() { return {
    "$": ["sc-select.css"]
  }; }
  static get properties() { return {
    "autocomplete": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "The input's autocomplete attribute."
      },
      "attribute": "autocomplete",
      "reflect": false
    },
    "placeholder": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Placeholder for no value"
      },
      "attribute": "placeholder",
      "reflect": false,
      "defaultValue": "''"
    },
    "searchPlaceholder": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Placeholder for search"
      },
      "attribute": "search-placeholder",
      "reflect": false,
      "defaultValue": "''"
    },
    "value": {
      "type": "string",
      "mutable": true,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "The input's value attribute."
      },
      "attribute": "value",
      "reflect": true,
      "defaultValue": "''"
    },
    "choices": {
      "type": "unknown",
      "mutable": true,
      "complexType": {
        "original": "Array<ChoiceItem>",
        "resolved": "ChoiceItem[]",
        "references": {
          "Array": {
            "location": "global"
          },
          "ChoiceItem": {
            "location": "import",
            "path": "../../../types"
          }
        }
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "The input's value attribute."
      },
      "defaultValue": "[]"
    },
    "unselect": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Can we unselect items."
      },
      "attribute": "unselect",
      "reflect": false,
      "defaultValue": "true"
    },
    "required": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "required",
      "reflect": true
    },
    "loading": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "loading",
      "reflect": false
    },
    "search": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Is search enabled?"
      },
      "attribute": "search",
      "reflect": false
    },
    "name": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "The input's name attribute."
      },
      "attribute": "name",
      "reflect": true
    },
    "help": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Some help text for the input."
      },
      "attribute": "help",
      "reflect": false
    },
    "label": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "The input's label."
      },
      "attribute": "label",
      "reflect": false
    },
    "size": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "'small' | 'medium' | 'large'",
        "resolved": "\"large\" | \"medium\" | \"small\"",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "The input's size."
      },
      "attribute": "size",
      "reflect": true,
      "defaultValue": "'medium'"
    },
    "position": {
      "type": "string",
      "mutable": false,
      "complexType": {
        "original": "'bottom-left' | 'bottom-right'",
        "resolved": "\"bottom-left\" | \"bottom-right\"",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "position",
      "reflect": false,
      "defaultValue": "'bottom-right'"
    },
    "invalid": {
      "type": "boolean",
      "mutable": true,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "This will be true when the control is in an invalid state. Validity is determined by props such as `type`,\n`required`, `minlength`, `maxlength`, and `pattern` using the browser's constraint validation API."
      },
      "attribute": "invalid",
      "reflect": true,
      "defaultValue": "false"
    },
    "open": {
      "type": "boolean",
      "mutable": true,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": "Is this open"
      },
      "attribute": "open",
      "reflect": false
    },
    "disabled": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "disabled",
      "reflect": false
    },
    "showParentLabel": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "show-parent-label",
      "reflect": false,
      "defaultValue": "true"
    },
    "hoist": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "hoist",
      "reflect": false,
      "defaultValue": "false"
    },
    "squared": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "squared",
      "reflect": false
    },
    "squaredBottom": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "squared-bottom",
      "reflect": false
    },
    "squaredTop": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "squared-top",
      "reflect": false
    },
    "squaredLeft": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "squared-left",
      "reflect": false
    },
    "squaredRight": {
      "type": "boolean",
      "mutable": false,
      "complexType": {
        "original": "boolean",
        "resolved": "boolean",
        "references": {}
      },
      "required": false,
      "optional": false,
      "docs": {
        "tags": [],
        "text": ""
      },
      "attribute": "squared-right",
      "reflect": false
    }
  }; }
  static get states() { return {
    "hasFocus": {},
    "searchTerm": {},
    "filteredChoices": {}
  }; }
  static get events() { return [{
      "method": "scSearch",
      "name": "scSearch",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": "Emitted whent the components search query changes"
      },
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      }
    }, {
      "method": "scOpen",
      "name": "scOpen",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": "Emitted whent the components search query changes"
      },
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      }
    }, {
      "method": "scClose",
      "name": "scClose",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": "Emitted whent the components search query changes"
      },
      "complexType": {
        "original": "string",
        "resolved": "string",
        "references": {}
      }
    }, {
      "method": "scBlur",
      "name": "scBlur",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": "Emitted when the control loses focus."
      },
      "complexType": {
        "original": "void",
        "resolved": "void",
        "references": {}
      }
    }, {
      "method": "scFocus",
      "name": "scFocus",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": "Emitted when the control gains focus."
      },
      "complexType": {
        "original": "void",
        "resolved": "void",
        "references": {}
      }
    }, {
      "method": "scChange",
      "name": "scChange",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": "Emitted when the control's value changes."
      },
      "complexType": {
        "original": "void",
        "resolved": "void",
        "references": {}
      }
    }, {
      "method": "scScrollEnd",
      "name": "scScrollEnd",
      "bubbles": true,
      "cancelable": true,
      "composed": true,
      "docs": {
        "tags": [],
        "text": "Emitted when the list scrolls to the end."
      },
      "complexType": {
        "original": "void",
        "resolved": "void",
        "references": {}
      }
    }]; }
  static get methods() { return {
    "setCustomValidity": {
      "complexType": {
        "signature": "(message: string) => Promise<void>",
        "parameters": [{
            "tags": [],
            "text": ""
          }],
        "references": {
          "Promise": {
            "location": "global"
          }
        },
        "return": "Promise<void>"
      },
      "docs": {
        "text": "Sets a custom validation message. If `message` is not empty, the field will be considered invalid.",
        "tags": []
      }
    },
    "reportValidity": {
      "complexType": {
        "signature": "() => Promise<boolean>",
        "parameters": [],
        "references": {
          "Promise": {
            "location": "global"
          }
        },
        "return": "Promise<boolean>"
      },
      "docs": {
        "text": "",
        "tags": []
      }
    }
  }; }
  static get elementRef() { return "el"; }
  static get watchers() { return [{
      "propName": "searchTerm",
      "methodName": "handleSearchChange"
    }, {
      "propName": "choices",
      "methodName": "handleSearchChange"
    }, {
      "propName": "value",
      "methodName": "handleValueChange"
    }, {
      "propName": "open",
      "methodName": "handleOpenChange"
    }]; }
  static get listeners() { return [{
      "name": "keydown",
      "method": "handleKeyDown",
      "target": undefined,
      "capture": false,
      "passive": false
    }]; }
}
