import { __esDecorate, __runInitializers } from "tslib";
/* eslint-disable @typescript-eslint/unified-signatures */
import { html } from 'lit';
import { queryRequestHeaders } from '@api-client/core/lib/definitions/HttpDefinitions.js';
import { Events as CoreEvents } from '@api-client/core/events/Events.js';
import { Property } from '@api-client/core/models/Property.js';
import { HeadersArray } from '@api-client/core/models/HeadersArray.js';
import { property, state } from 'lit/decorators.js';
import { live } from 'lit/directives/live.js';
import ApiElement from '../ApiElement.js';
import { EventTypes } from '../../events/EventTypes.js';
import { SnackNotifications } from '../../md/notification/SnackNotifications.js';
import '../../md/button/ui-button.js';
import '../../md/icon-button/ui-icon-button.js';
import '../../md/dropdown-list/ui-dropdown-list.js';
import '../../md/list/ui-list.js';
import '../../md/list/ui-list-item.js';
import '../../md/switch/ui-switch.js';
import '../../md/divider/ui-divider.js';
import '../../md/icons/ui-icon.js';
const nameSuggestions = queryRequestHeaders().map((item) => item.key);
let HeadersForm = (() => {
    let _classSuper = ApiElement;
    let _readOnly_decorators;
    let _readOnly_initializers = [];
    let _readOnly_extraInitializers = [];
    let _text_decorators;
    let _text_initializers = [];
    let _text_extraInitializers = [];
    let _model_decorators;
    let _model_initializers = [];
    let _model_extraInitializers = [];
    let _internalModel_decorators;
    let _internalModel_initializers = [];
    let _internalModel_extraInitializers = [];
    let _textValue_decorators;
    let _textValue_initializers = [];
    let _textValue_extraInitializers = [];
    return class HeadersForm extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _readOnly_decorators = [property({ type: Boolean, reflect: true })];
            _text_decorators = [property({ type: Boolean, reflect: true })];
            _model_decorators = [property({ type: Array })];
            _internalModel_decorators = [state()];
            _textValue_decorators = [state()];
            __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, _text_decorators, { kind: "accessor", name: "text", static: false, private: false, access: { has: obj => "text" in obj, get: obj => obj.text, set: (obj, value) => { obj.text = value; } }, metadata: _metadata }, _text_initializers, _text_extraInitializers);
            __esDecorate(this, null, _model_decorators, { kind: "accessor", name: "model", static: false, private: false, access: { has: obj => "model" in obj, get: obj => obj.model, set: (obj, value) => { obj.model = value; } }, metadata: _metadata }, _model_initializers, _model_extraInitializers);
            __esDecorate(this, null, _internalModel_decorators, { kind: "accessor", name: "internalModel", static: false, private: false, access: { has: obj => "internalModel" in obj, get: obj => obj.internalModel, set: (obj, value) => { obj.internalModel = value; } }, metadata: _metadata }, _internalModel_initializers, _internalModel_extraInitializers);
            __esDecorate(this, null, _textValue_decorators, { kind: "accessor", name: "textValue", static: false, private: false, access: { has: obj => "textValue" in obj, get: obj => obj.textValue, set: (obj, value) => { obj.textValue = value; } }, metadata: _metadata }, _textValue_initializers, _textValue_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #readOnly_accessor_storage = __runInitializers(this, _readOnly_initializers, false
        /**
         * Whether the text editor is rendered instead of the form.
         * @attribute
         */
        );
        /**
         * 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; }
        #text_accessor_storage = (__runInitializers(this, _readOnly_extraInitializers), __runInitializers(this, _text_initializers, false
        /**
         * The headers model.
         */
        ));
        /**
         * Whether the text editor is rendered instead of the form.
         * @attribute
         */
        get text() { return this.#text_accessor_storage; }
        set text(value) { this.#text_accessor_storage = value; }
        #model_accessor_storage = (__runInitializers(this, _text_extraInitializers), __runInitializers(this, _model_initializers, []));
        /**
         * The headers model.
         */
        get model() { return this.#model_accessor_storage; }
        set model(value) { this.#model_accessor_storage = value; }
        #internalModel_accessor_storage = (__runInitializers(this, _model_extraInitializers), __runInitializers(this, _internalModel_initializers, new HeadersArray()));
        get internalModel() { return this.#internalModel_accessor_storage; }
        set internalModel(value) { this.#internalModel_accessor_storage = value; }
        #textValue_accessor_storage = (__runInitializers(this, _internalModel_extraInitializers), __runInitializers(this, _textValue_initializers, void 0));
        get textValue() { return this.#textValue_accessor_storage; }
        set textValue(value) { this.#textValue_accessor_storage = value; }
        autocompleteMap = (__runInitializers(this, _textValue_extraInitializers), {});
        constructor() {
            super();
            this.handleGlobalContentType = this.handleGlobalContentType.bind(this);
        }
        connectedCallback() {
            super.connectedCallback();
            this.eventsTarget.addEventListener(EventTypes.Http.Request.State.contentTypeChange, this.handleGlobalContentType);
        }
        disconnectedCallback() {
            super.disconnectedCallback();
            this.eventsTarget.removeEventListener(EventTypes.Http.Request.State.contentTypeChange, this.handleGlobalContentType);
        }
        update(cp) {
            if (cp.has('model')) {
                if (this.model) {
                    this.internalModel = HeadersArray.from(this.model);
                    this.model.forEach((item, index) => this.setupValueAutocomplete(item.name, index));
                }
                else {
                    this.internalModel = new HeadersArray();
                }
            }
            if (cp.has('text')) {
                if (this.text) {
                    this.textValue = this.internalModel.toRawString();
                }
                else {
                    this.textValue = undefined;
                }
            }
            super.update(cp);
        }
        setupHeaders(headers) {
            this.autocompleteMap = {};
            if (headers) {
                this.internalModel = HeadersArray.fromHeaders(headers);
                this.model = this.internalModel.toJSON();
            }
            else {
                this.internalModel = new HeadersArray();
                this.model = [];
            }
            this.requestUpdate();
        }
        /**
         * Updates header value. It the header does not exists at the position it adds a new header.
         * @param nameOrIndex Header name or index
         * @param value Header value
         */
        updateHeaderValue(nameOrIndex, value) {
            const { internalModel } = this;
            let index;
            if (typeof nameOrIndex === 'number') {
                index = nameOrIndex;
            }
            else {
                const lowerName = nameOrIndex.toLowerCase();
                index = internalModel.findIndex((item) => (item.name || '').toLocaleLowerCase() === lowerName);
            }
            if (index === -1) {
                if (typeof nameOrIndex === 'string') {
                    this.addHeader(nameOrIndex, value);
                }
                else {
                    this.addHeader('', value);
                }
                this.propagateModelChange();
                return;
            }
            internalModel[index].value = value;
            this.model[index].value = value;
            if (this.text) {
                this.textValue = internalModel.toRawString();
            }
            this.propagateModelChange();
            this.requestUpdate();
        }
        /**
         * Updates header name. It the header does not exists at the position it adds a new header.
         * @param nameOrIndex The header name or index
         * @param value The header name
         */
        updateHeaderName(nameOrIndex, value) {
            const { internalModel } = this;
            let index;
            if (typeof nameOrIndex === 'number') {
                index = nameOrIndex;
            }
            else {
                const lowerName = nameOrIndex.toLowerCase();
                index = internalModel.findIndex((item) => (item.name || '').toLocaleLowerCase() === lowerName);
            }
            if (index === -1) {
                this.addHeader(value);
                return;
            }
            internalModel[index].name = value;
            this.model[index].name = value;
            this.setupValueAutocomplete(value, index);
            if (this.text) {
                this.textValue = internalModel.toRawString();
            }
            this.propagateModelChange();
            this.requestUpdate();
        }
        setupValueAutocomplete(name, index) {
            const query = name.toLowerCase();
            const items = queryRequestHeaders(query);
            const item = items.find((i) => i.key.toLowerCase() === query);
            if (item && item.autocomplete) {
                this.autocompleteMap[`header-value-${index}`] = item.autocomplete;
            }
            else {
                // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
                delete this.autocompleteMap[`header-value-${index}`];
            }
        }
        /**
         * Removes a header from the list.
         * @param nameOrIndex Header name or its index.
         */
        removeHeader(nameOrIndex) {
            const { internalModel } = this;
            let index;
            if (typeof nameOrIndex === 'number') {
                index = nameOrIndex;
            }
            else {
                const lowerName = nameOrIndex.toLowerCase();
                index = internalModel.findIndex((item) => (item.name || '').toLocaleLowerCase() === lowerName);
            }
            if (index < 0) {
                return;
            }
            internalModel.splice(index, 1);
            this.model.splice(index, 1);
            // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
            delete this.autocompleteMap[`header-value-${index}`];
            if (this.text) {
                this.textValue = internalModel.toRawString();
            }
            this.propagateModelChange();
            this.requestUpdate();
        }
        /**
         * Adds a header to the list of headers
         */
        addHeader(name = '', value = '') {
            const prop = Property.String(name, value);
            this.internalModel.push(prop);
            this.model.push(prop.toJSON());
            // the value hasn't actually changed here so no events
            this.requestUpdate();
        }
        /**
         * Removes all parameters.
         */
        clear() {
            this.model.splice(0);
            this.internalModel.splice(0);
            this.propagateModelChange();
            this.autocompleteMap = {};
            if (this.text) {
                this.textValue = '';
            }
            this.requestUpdate();
        }
        /**
         * Toggles enabled state of a header.
         */
        toggleHeader(index, enabled) {
            this.model[index].enabled = enabled;
            this.internalModel[index].enabled = enabled;
            if (this.text) {
                this.textValue = this.internalModel.toRawString();
            }
            this.propagateModelChange();
        }
        /**
         * Toggles the state between form and text editors.
         */
        toggle() {
            this.text = !this.text;
        }
        async copyToClipboard() {
            if (!navigator.clipboard) {
                throw new Error(`Clipboard API not supported.`);
            }
            await navigator.clipboard.writeText(this.internalModel.toString());
            SnackNotifications.notify('Headers copied to clipboard.', 2000);
            CoreEvents.Telemetry.event({
                category: 'Usage',
                action: 'Click',
                label: 'Headers editor clipboard copy',
            }, this);
        }
        handleGlobalContentType(e) {
            const { value } = e.detail;
            this.updateHeaderValue('content-type', value);
        }
        /**
         * Updates the `value` from the current model and dispatches the value change event
         */
        propagateModelChange() {
            this.notifyChange();
        }
        async focusLast() {
            await this.updateComplete;
            const row = this.shadowRoot?.querySelector('.param-row:last-of-type');
            if (!row) {
                return;
            }
            try {
                const node = row.querySelector('.name-input input');
                if (node) {
                    node.focus();
                }
            }
            catch {
                // ...
            }
        }
        handleAdd() {
            this.addHeader();
        }
        handleOption(e) {
            const element = e.detail.item;
            const { option } = element.dataset;
            if (!option) {
                return;
            }
            if (option === 'clear') {
                this.clear();
            }
            else if (option === 'text-editor') {
                this.toggle();
            }
        }
        handleNameChange(e) {
            const input = e.target;
            const index = Number(input.dataset.index);
            if (Number.isNaN(index)) {
                return;
            }
            this.updateHeaderName(index, input.value);
            input.checkValidity();
        }
        handleValueChange(e) {
            const input = e.target;
            const index = Number(input.dataset.index);
            if (Number.isNaN(index)) {
                return;
            }
            this.updateHeaderValue(index, input.value);
            input.checkValidity();
        }
        handleRemove(e) {
            const node = e.currentTarget;
            const index = Number(node.dataset.index);
            if (Number.isNaN(index)) {
                return;
            }
            this.removeHeader(index);
        }
        handleToggle(e) {
            const node = e.currentTarget;
            const index = Number(node.dataset.index);
            if (Number.isNaN(index)) {
                return;
            }
            this.toggleHeader(index, !!node.checked);
        }
        handleText(e) {
            const input = e.target;
            const { value } = input;
            this.setupHeaders(value);
            this.textValue = value;
            this.notifyChange();
        }
        handleCopy() {
            this.copyToClipboard();
        }
        render() {
            return html `
      ${this.renderActionButtons()} ${this.renderEditor()} ${this.renderNamesList()} ${this.renderAutocompleteLists()}
    `;
        }
        renderActionButtons() {
            return html `
      <div class="editor-actions">
        <ui-button color="tonal" @click="${this.handleAdd}">Add</ui-button>
        <ui-button color="tonal" @click="${this.handleCopy}">Copy</ui-button>
        <ui-dropdown-list @select="${this.handleOption}" closeOnOutsideClick class="dropdown">
          <ui-icon-button aria-label="Additional options"
            ><ui-icon icon="moreVert" role="presentation"></ui-icon
          ></ui-icon-button>
          <ui-list slot="dropdown" role="menu">
            <ui-list-item
              role="menuitem"
              data-option="text-editor"
              image="icon"
              title="Toggles between a text editor and a form"
            >
              <ui-icon role="presentation" slot="start" icon="${this.text ? 'checkBox' : 'checkBoxBlank'}"></ui-icon>
              Text editor
            </ui-list-item>
            <ui-divider type="inset"></ui-divider>
            <ui-list-item
              role="menuitem"
              data-option="clear"
              image="icon"
              title="Removes all parameters from the editor"
            >
              <ui-icon role="presentation" slot="start" icon="deleteOutline"></ui-icon>
              Clear form
            </ui-list-item>
          </ui-list>
        </ui-dropdown-list>
      </div>
    `;
        }
        renderEditor() {
            if (this.text) {
                return this.renderText();
            }
            return this.renderList();
        }
        renderText() {
            const { textValue = '' } = this;
            return html `
      <div class="text-input">
        <div class="error" role="presentation">Invalid character</div>
        <div class="state"></div>
        <textarea .value="${live(textValue)}" data-enable-grammarly="false" @change="${this.handleText}"></textarea>
      </div>
    `;
        }
        renderList() {
            const items = this.internalModel;
            if (!Array.isArray(items) || !items.length) {
                return this.renderEmptyMessage();
            }
            return this.renderParams(items);
        }
        renderEmptyMessage() {
            return html ` <div class="empty-message">No parameters added</div> `;
        }
        renderParams(items) {
            return html ` ${items.map((item, index) => this.renderParam(item, index))} `;
        }
        renderParam(item, index) {
            return html `
      <div class="param-row">
        ${this.renderToggleButton(item, index)} ${this.renderNameField(item, index)}
        ${this.renderValueField(item, index)} ${this.renderDeleteParameter(index)}
      </div>
    `;
        }
        renderToggleButton(item, index) {
            return html `
      <ui-switch
        .checked="${!!item.enabled}"
        @change="${this.handleToggle}"
        aria-label="Toggle this parameter"
        data-index="${index}"
      ></ui-switch>
    `;
        }
        renderDeleteParameter(index) {
            return html `
      <ui-icon-button aria-label="Delete this parameter" data-index="${index}" @click="${this.handleRemove}">
        <ui-icon icon="deleteOutline"></ui-icon>
      </ui-icon-button>
    `;
        }
        renderNameField(item, index) {
            return html `
      <div class="input-wrapper name-input">
        <div class="error" role="presentation">Invalid character</div>
        <div class="state"></div>
        <input
          type="text"
          name="name"
          autocomplete="off"
          .value="${live(item.name)}"
          data-index="${index}"
          ?readOnly="${this.readOnly}"
          pattern="\\S*"
          aria-label="Parameter name value"
          @change="${this.handleNameChange}"
          list="names-list"
        />
      </div>
    `;
        }
        renderValueField(item, index) {
            return html `
      <div class="input-wrapper value-input">
        <div class="error" role="presentation">Invalid character</div>
        <div class="state"></div>
        <input
          type="text"
          name="value"
          .value="${live(item.value)}"
          data-index="${index}"
          ?readOnly="${this.readOnly}"
          aria-label="Parameter value"
          @change="${this.handleValueChange}"
          list="header-value-${index}"
        />
      </div>
    `;
        }
        renderNamesList() {
            return html `
      <datalist id="names-list">${nameSuggestions.map((i) => html `<option value="${i}"></option>`)}</datalist>
    `;
        }
        renderAutocompleteLists() {
            const { autocompleteMap } = this;
            return Object.keys(autocompleteMap).map((key) => html `
        <datalist id="${key}">${autocompleteMap[key].map((i) => html `<option value="${i}"></option>`)}</datalist>
      `);
        }
    };
})();
/**
 * An editor that creates HTTP headers as a data model that can be translated into a headers string.
 */
export default HeadersForm;
//# sourceMappingURL=HeadersForm.js.map