import { __esDecorate, __runInitializers } from "tslib";
import { html } from 'lit';
import { classMap } from 'lit/directives/class-map.js';
import { styleMap } from 'lit/directives/style-map.js';
import { eventOptions, property, query, state } from 'lit/decorators.js';
import { sortUrls } from '../../lib/http/Url.js';
import { Events } from '../../events/Events.js';
import { EventTypes } from '../../events/EventTypes.js';
import ApiElement from '../ApiElement.js';
import { positionOverlay } from '../../lib/ElementPositioning.js';
import '../../md/listbox/ui-listbox.js';
import '../../md/list/ui-list-item.js';
import '../../md/divider/ui-divider.js';
const NAVIGATION_KEYS = {
    ArrowDown: 'ArrowDown',
    ArrowUp: 'ArrowUp',
    Home: 'Home',
    End: 'End',
    Escape: 'Escape',
    Enter: 'Enter',
    Space: ' ',
};
let UrlInput = (() => {
    let _classSuper = ApiElement;
    let _instanceExtraInitializers = [];
    let _value_decorators;
    let _value_initializers = [];
    let _value_extraInitializers = [];
    let _readOnly_decorators;
    let _readOnly_initializers = [];
    let _readOnly_extraInitializers = [];
    let _suggestionsOpened_decorators;
    let _suggestionsOpened_initializers = [];
    let _suggestionsOpened_extraInitializers = [];
    let _suggestions_decorators;
    let _suggestions_initializers = [];
    let _suggestions_extraInitializers = [];
    let _renderedSuggestions_decorators;
    let _renderedSuggestions_initializers = [];
    let _renderedSuggestions_extraInitializers = [];
    let _input_decorators;
    let _input_initializers = [];
    let _input_extraInitializers = [];
    let _suggestionsContainer_decorators;
    let _suggestionsContainer_initializers = [];
    let _suggestionsContainer_extraInitializers = [];
    let _suggestionsList_decorators;
    let _suggestionsList_initializers = [];
    let _suggestionsList_extraInitializers = [];
    let _handleSuggestionClick_decorators;
    return class UrlInput extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _value_decorators = [property({ type: String })];
            _readOnly_decorators = [property({ type: Boolean, reflect: true })];
            _suggestionsOpened_decorators = [state()];
            _suggestions_decorators = [state()];
            _renderedSuggestions_decorators = [state()];
            _input_decorators = [query('.main-input')];
            _suggestionsContainer_decorators = [query('.suggestions')];
            _suggestionsList_decorators = [query('ui-listbox.url-autocomplete')];
            _handleSuggestionClick_decorators = [eventOptions({ capture: true })];
            __esDecorate(this, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
            __esDecorate(this, null, _readOnly_decorators, { kind: "accessor", name: "readOnly", static: false, private: false, access: { has: obj => "readOnly" in obj, get: obj => obj.readOnly, set: (obj, value) => { obj.readOnly = value; } }, metadata: _metadata }, _readOnly_initializers, _readOnly_extraInitializers);
            __esDecorate(this, null, _suggestionsOpened_decorators, { kind: "accessor", name: "suggestionsOpened", static: false, private: false, access: { has: obj => "suggestionsOpened" in obj, get: obj => obj.suggestionsOpened, set: (obj, value) => { obj.suggestionsOpened = value; } }, metadata: _metadata }, _suggestionsOpened_initializers, _suggestionsOpened_extraInitializers);
            __esDecorate(this, null, _suggestions_decorators, { kind: "accessor", name: "suggestions", static: false, private: false, access: { has: obj => "suggestions" in obj, get: obj => obj.suggestions, set: (obj, value) => { obj.suggestions = value; } }, metadata: _metadata }, _suggestions_initializers, _suggestions_extraInitializers);
            __esDecorate(this, null, _renderedSuggestions_decorators, { kind: "accessor", name: "renderedSuggestions", static: false, private: false, access: { has: obj => "renderedSuggestions" in obj, get: obj => obj.renderedSuggestions, set: (obj, value) => { obj.renderedSuggestions = value; } }, metadata: _metadata }, _renderedSuggestions_initializers, _renderedSuggestions_extraInitializers);
            __esDecorate(this, null, _input_decorators, { kind: "accessor", name: "input", static: false, private: false, access: { has: obj => "input" in obj, get: obj => obj.input, set: (obj, value) => { obj.input = value; } }, metadata: _metadata }, _input_initializers, _input_extraInitializers);
            __esDecorate(this, null, _suggestionsContainer_decorators, { kind: "accessor", name: "suggestionsContainer", static: false, private: false, access: { has: obj => "suggestionsContainer" in obj, get: obj => obj.suggestionsContainer, set: (obj, value) => { obj.suggestionsContainer = value; } }, metadata: _metadata }, _suggestionsContainer_initializers, _suggestionsContainer_extraInitializers);
            __esDecorate(this, null, _suggestionsList_decorators, { kind: "accessor", name: "suggestionsList", static: false, private: false, access: { has: obj => "suggestionsList" in obj, get: obj => obj.suggestionsList, set: (obj, value) => { obj.suggestionsList = value; } }, metadata: _metadata }, _suggestionsList_initializers, _suggestionsList_extraInitializers);
            __esDecorate(this, null, _handleSuggestionClick_decorators, { kind: "method", name: "handleSuggestionClick", static: false, private: false, access: { has: obj => "handleSuggestionClick" in obj, get: obj => obj.handleSuggestionClick }, metadata: _metadata }, null, _instanceExtraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #value_accessor_storage = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _value_initializers, 'http://'
        /**
         * When set the editor is in read only mode.
         * @attribute
         */
        ));
        /**
         * The current URL value.
         * @attribute
         */
        get value() { return this.#value_accessor_storage; }
        set value(value) { this.#value_accessor_storage = value; }
        #readOnly_accessor_storage = (__runInitializers(this, _value_extraInitializers), __runInitializers(this, _readOnly_initializers, false
        /**
         * Whether the URL suggestions are rendered.
         */
        ));
        /**
         * When set the editor is in read only mode.
         * @attribute
         */
        get readOnly() { return this.#readOnly_accessor_storage; }
        set readOnly(value) { this.#readOnly_accessor_storage = value; }
        #suggestionsOpened_accessor_storage = (__runInitializers(this, _readOnly_extraInitializers), __runInitializers(this, _suggestionsOpened_initializers, false));
        /**
         * Whether the URL suggestions are rendered.
         */
        get suggestionsOpened() { return this.#suggestionsOpened_accessor_storage; }
        set suggestionsOpened(value) { this.#suggestionsOpened_accessor_storage = value; }
        #suggestions_accessor_storage = (__runInitializers(this, _suggestionsOpened_extraInitializers), __runInitializers(this, _suggestions_initializers, void 0));
        get suggestions() { return this.#suggestions_accessor_storage; }
        set suggestions(value) { this.#suggestions_accessor_storage = value; }
        #renderedSuggestions_accessor_storage = (__runInitializers(this, _suggestions_extraInitializers), __runInitializers(this, _renderedSuggestions_initializers, void 0));
        get renderedSuggestions() { return this.#renderedSuggestions_accessor_storage; }
        set renderedSuggestions(value) { this.#renderedSuggestions_accessor_storage = value; }
        previousValue = __runInitializers(this, _renderedSuggestions_extraInitializers);
        isFocused = false;
        #input_accessor_storage = __runInitializers(this, _input_initializers, void 0);
        get input() { return this.#input_accessor_storage; }
        set input(value) { this.#input_accessor_storage = value; }
        #suggestionsContainer_accessor_storage = (__runInitializers(this, _input_extraInitializers), __runInitializers(this, _suggestionsContainer_initializers, void 0));
        get suggestionsContainer() { return this.#suggestionsContainer_accessor_storage; }
        set suggestionsContainer(value) { this.#suggestionsContainer_accessor_storage = value; }
        #suggestionsList_accessor_storage = (__runInitializers(this, _suggestionsContainer_extraInitializers), __runInitializers(this, _suggestionsList_initializers, void 0));
        get suggestionsList() { return this.#suggestionsList_accessor_storage; }
        set suggestionsList(value) { this.#suggestionsList_accessor_storage = value; }
        /**
         * Computed positioning styles for the suggestion box.
         */
        suggestionStyles = __runInitializers(this, _suggestionsList_extraInitializers);
        constructor() {
            super();
            this.handleUrlChange = this.handleUrlChange.bind(this);
            this.handleHistoryDeleted = this.handleHistoryDeleted.bind(this);
            this.handleHistoryClear = this.handleHistoryClear.bind(this);
        }
        connectedCallback() {
            super.connectedCallback();
            const { eventsTarget } = this;
            eventsTarget.addEventListener(EventTypes.Http.Request.State.urlChange, this.handleUrlChange);
            eventsTarget.addEventListener(EventTypes.HttpClient.Model.UrlHistory.State.delete, this.handleHistoryDeleted);
            eventsTarget.addEventListener(EventTypes.HttpClient.Model.UrlHistory.State.clear, this.handleHistoryClear);
        }
        disconnectedCallback() {
            super.disconnectedCallback();
            const { eventsTarget } = this;
            eventsTarget.removeEventListener(EventTypes.Http.Request.State.urlChange, this.handleUrlChange);
            eventsTarget.removeEventListener(EventTypes.HttpClient.Model.UrlHistory.State.delete, this.handleHistoryDeleted);
            eventsTarget.removeEventListener(EventTypes.HttpClient.Model.UrlHistory.State.clear, this.handleHistoryClear);
        }
        /**
         * A handler that is called on input
         */
        notifyChange() {
            Events.Http.Request.State.urlChange(this.value, this);
            super.notifyChange();
        }
        /**
         * A handler for the `url-value-changed` event.
         * If this element is not the source of the event then it will update the `value` property.
         * It's to be used besides the Polymer's data binding system.
         */
        handleUrlChange(e) {
            if (e.composedPath()[0] === this || this.readOnly) {
                return;
            }
            const { value } = e.detail;
            if (value !== this.value) {
                this.value = value;
                super.notifyChange();
            }
        }
        /**
         * Queries the data model for history data and sets the suggestions
         * @param q User query from the input field
         */
        async readAutocomplete(q) {
            try {
                const value = await Events.HttpClient.Model.UrlHistory.query(q, this);
                this.suggestions = value && value.items.filter((i) => !!i).map((i) => i.doc);
            }
            catch {
                this.suggestions = undefined;
            }
        }
        /**
         * Validates the element.
         */
        checkValidity() {
            const element = this.input;
            if (!element) {
                return true;
            }
            return element.validity.valid;
        }
        /**
         * @param e A handler for either main input or the details editor value change
         */
        handleInput(e) {
            if (this.readOnly) {
                return;
            }
            const node = e.target;
            this.previousValue = this.value;
            this.value = node.value;
            this.renderSuggestions();
        }
        handleInputChange() {
            if (this.readOnly) {
                return;
            }
            this.notifyChange();
        }
        handlerFocusBlur(e) {
            this.isFocused = e.type === 'focus';
            // this.requestUpdate();
            if (this.isFocused && !this.suggestionsOpened) {
                this.renderSuggestions();
            }
            else if (!this.isFocused && this.suggestionsOpened) {
                // requestAnimationFrame(() => {
                //   this.suggestionsOpened = false;
                // });
            }
        }
        /**
         * Triggers URL suggestions rendering.
         * If there are suggestions to render this will enable the dropdown.
         */
        async renderSuggestions() {
            const { value = '' } = this;
            if (this.previousValue && value.startsWith(this.previousValue || '')) {
                this.filterSuggestions();
                return;
            }
            await this.readAutocomplete(value);
            this.filterSuggestions();
        }
        /**
         * Performs the query on the current suggestions and, if any, renders them.
         */
        async filterSuggestions() {
            const items = this.suggestions;
            if (!Array.isArray(items) || !items.length) {
                this.toggleSuggestions(false);
                return;
            }
            const { value = '' } = this;
            const q = String(value).toLowerCase();
            const rendered = items.filter((i) => i.key.toLowerCase().includes(q));
            if (!rendered.length) {
                this.toggleSuggestions(false);
                return;
            }
            if (rendered.length === 1 && rendered[0].key.toLowerCase() === q) {
                this.toggleSuggestions(false);
                return;
            }
            sortUrls(rendered, q);
            this.renderedSuggestions = rendered;
            this.toggleSuggestions(true);
            this.requestUpdate();
        }
        toggleSuggestions(opened) {
            if (!opened) {
                this.suggestionsOpened = false;
                this.input.focus();
            }
            else {
                this.suggestionStyles = positionOverlay(this.suggestionsContainer, this.input, {
                    vertical: 'top',
                    noOverlap: true,
                    constrain: true,
                    matchAnchorWidth: true,
                });
                this.suggestionsOpened = true;
            }
        }
        handleSuggestionClick(e) {
            const path = e.composedPath();
            const target = path.find((i) => !!i.classList && i.classList.contains('remove-suggestion'));
            if (!target) {
                return;
            }
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            const { key } = target.dataset;
            if (!key) {
                return;
            }
            Events.HttpClient.Model.UrlHistory.delete(key, this);
        }
        handleSuggestionSelect(e) {
            const item = e.detail.item;
            const { key } = item.dataset;
            if (!key) {
                return;
            }
            this.selectSuggestion(key);
        }
        selectSuggestion(key) {
            this.value = key;
            this.toggleSuggestions(false);
            this.notifyChange();
        }
        /**
         * Removes all stored history URLs.
         */
        clearSuggestionsHandler(e) {
            e.preventDefault();
            e.stopPropagation();
            Events.HttpClient.Model.UrlHistory.clear(this);
        }
        handleHistoryDeleted(e) {
            const items = this.suggestions;
            if (!Array.isArray(items)) {
                return;
            }
            const url = e.detail;
            const index = items.findIndex((i) => i.key === url);
            if (index >= 0) {
                items.splice(index, 1);
            }
            if (this.suggestionsOpened && this.renderedSuggestions) {
                const renderedIndex = this.renderedSuggestions.findIndex((i) => i.key === url);
                if (renderedIndex >= 0) {
                    this.renderedSuggestions.splice(renderedIndex, 1);
                    this.requestUpdate();
                }
            }
        }
        handleHistoryClear() {
            this.suggestions = undefined;
            this.renderedSuggestions = undefined;
            this.toggleSuggestions(false);
        }
        /**
         * We handle key events here as we never focus on the list.
         */
        handleKeydown(event) {
            if (Object.values(NAVIGATION_KEYS).indexOf(event.key) === -1)
                return;
            if (event.key === NAVIGATION_KEYS.Escape) {
                this.toggleSuggestions(false);
            }
            else if (event.key === NAVIGATION_KEYS.ArrowDown) {
                event.preventDefault();
                if (this.suggestionsOpened) {
                    this.suggestionsList.highlightNext();
                }
                else {
                    this.filterSuggestions();
                }
            }
            else if (event.key === NAVIGATION_KEYS.ArrowUp) {
                event.preventDefault();
                if (this.suggestionsOpened) {
                    this.suggestionsList.highlightPrevious();
                }
                else {
                    this.filterSuggestions();
                }
            }
            else if (event.key === NAVIGATION_KEYS.Home) {
                if (this.suggestionsOpened) {
                    event.preventDefault();
                    this.suggestionsList.highlightFirst();
                }
            }
            else if (event.key === NAVIGATION_KEYS.End) {
                if (this.suggestionsOpened) {
                    event.preventDefault();
                    this.suggestionsList.highlightLast();
                }
            }
            else if (event.key === NAVIGATION_KEYS.Enter) {
                event.preventDefault();
                if (this.suggestionsOpened) {
                    const list = this.suggestionsList;
                    const item = list.highlightListItem;
                    if (item) {
                        list.notifySelect(item);
                    }
                }
                else {
                    Events.Http.Request.send(this);
                }
            }
        }
        render() {
            const classes = {
                container: true,
                autocomplete: this.suggestionsOpened,
            };
            return html ` <div class="${classMap(classes)}">${this.renderInput()} ${this.renderAutocomplete()}</div>`;
        }
        /**
         * @returns A template for the main input element
         */
        renderInput() {
            const { value } = this;
            return html `
      <div class="input-wrapper">
        <input
          .value="${value}"
          class="main-input"
          required
          placeholder="Request URL"
          id="mainInput"
          autocomplete="off"
          spellcheck="false"
          @focus="${this.handlerFocusBlur}"
          @blur="${this.handlerFocusBlur}"
          @input="${this.handleInput}"
          @change="${this.handleInputChange}"
          @keydown="${this.handleKeydown}"
          aria-label="The URL value"
          ?readonly="${this.readOnly}"
        />
      </div>
    `;
        }
        /**
         * @returns A template for the autocomplete element
         */
        renderAutocomplete() {
            let opened = this.suggestionsOpened;
            const items = this.renderedSuggestions;
            if (opened && (!items || !items.length)) {
                opened = false;
            }
            const classes = {
                suggestions: true,
                opened,
            };
            return html `
      <div class="${classMap(classes)}" style="${styleMap(this.suggestionStyles || {})}">
        <ui-listbox
          class="url-autocomplete"
          role="listbox"
          aria-label="Suggested URLs from your history."
          tabindex="-1"
          @select="${this.handleSuggestionSelect}"
          @click="${this.handleSuggestionClick}"
        >
          ${this.renderSuggestionList()}
        </ui-listbox>
        <ui-divider></ui-divider>
        <p class="clear-all-history">
          <span class="clear-all-history-label" @click="${this.clearSuggestionsHandler}">Clear all history</span>
        </p>
      </div>
    `;
        }
        /**
         * @returns The template for the suggestions list.
         */
        renderSuggestionList() {
            const items = this.renderedSuggestions;
            if (!Array.isArray(items) || !items.length) {
                return '';
            }
            return items.map((i) => this.renderSuggestionItem(i));
        }
        /**
         * @returns The template for an URL suggestion item.
         */
        renderSuggestionItem(item) {
            const { key } = item;
            return html ` <ui-list-item class="suggestion-item" data-key="${item.key}" role="option" aria-selected="false">
      <div>${key}</div>
      <span slot="end-text" class="remove-suggestion" data-key="${key}">Remove</span>
    </ui-list-item>`;
        }
    };
})();
/**
 * The request URL editor
 *
 * The element renders an editor for a HTTP request editor.
 *
 * @fires change - When the value change through user interaction. This event does not bubble.
 * @fires requeststateurlchange - When the value change through user interaction. This even bubbles and is cancelable.
 * @fires requestsend - A bubbling event when the user pressed Enter to send the request.
 */
export default UrlInput;
//# sourceMappingURL=UrlInput.js.map