import { __decorate } from "tslib";
import { DataPropertyTypes, EventUtils } from "@api-client/core/build/browser.js";
import { css, html, nothing } from "lit";
import { property, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { live } from "lit/directives/live.js";
import ApiElement from "../ApiElement.js";
import EditorCommon from './CommonStyles.js';
import theme from '../theme.js';
import typography from "../../styles/m3/typography.module.js";
import '../../define/ui/ui-text-field.js';
import '../../define/ui/ui-checkbox.js';
import '../../define/ui/ui-divider.js';
import '../../define/ui/ui-icon-button.js';
import '../../define/ui/ui-list.js';
import '../../define/ui/ui-list-item.js';
import '../../define/ui/ui-dropdown-list.js';
import '../../define/ui/ui-icon.js';
export default class PropertyFormElement extends ApiElement {
    static get styles() {
        return [
            theme,
            EditorCommon,
            typography,
            css `
      :host {
        display: block;
      }

      .no-description {
        margin: 1.2rem 20px;
      }

      .enum-values {
        margin: 0px 0px 20px;
        padding: 0px 0px 20px;
      }

      .input-item {
        display: flex;
        list-style: none;
        margin: 4px 20px;
      }

      .input.list-input {
        margin: 0;
        flex: 1;
      }
      `
        ];
    }
    willUpdate(cp) {
        if (cp.has('key')) {
            this._computeProperty();
        }
    }
    /**
     * Computes the view value for the property.
     * This should be called before the update is complete so this won't trigger another update.
     */
    _computeProperty() {
        const { root, key } = this;
        if (!root || !key) {
            this._property = undefined;
            return;
        }
        this._property = root.definitions.properties.find(i => i.key === key);
    }
    _infoChangeHandler(e) {
        const { _property } = this;
        if (!_property) {
            return;
        }
        const node = e.target;
        const { name, value } = node;
        if (!['name', 'description', 'displayName'].includes(name)) {
            return;
        }
        if (name === 'name' && !value) {
            return;
        }
        _property.info[name] = value;
        this.notifyChange();
        this.requestUpdate();
    }
    setType(value) {
        const { _property } = this;
        if (!_property) {
            return;
        }
        if (_property.type === value) {
            return;
        }
        _property.type = value;
        this.notifyChange();
        this.requestUpdate();
    }
    typeSelectHandler(e) {
        const target = e.detail.item;
        if (!target) {
            return;
        }
        const value = target.dataset.value;
        if (value) {
            this.setType(value);
        }
    }
    _checkedHandler(e) {
        const { _property } = this;
        if (!_property) {
            return;
        }
        const input = e.target;
        const name = input.name;
        const { checked } = input;
        if (_property[name] === checked) {
            return;
        }
        _property[name] = checked;
        this.notifyChange();
        this.requestUpdate();
    }
    defaultValueHandler(e) {
        const target = e.target;
        let value;
        if (target.localName === 'ui-dropdown-list') {
            const item = e.detail.item;
            value = item.dataset.value;
            this.requestUpdate();
        }
        else {
            const input = e.target;
            value = input.value;
        }
        this.setDefaultValue(value);
    }
    /**
     * Sets a new defaultValue on the property schema
     * @param value The default value to set. Empty to remove.
     */
    setDefaultValue(value) {
        const { _property } = this;
        if (!_property) {
            return;
        }
        const adapted = this._getAdapted(_property);
        const schema = adapted.schema;
        if (value === schema.defaultValue) {
            return;
        }
        if (value) {
            schema.defaultValue = value;
        }
        else {
            delete schema.defaultValue;
        }
        this.notifyChange();
    }
    /**
     * Adds an example value to the current property's schema.
     * @param value The example value to add
     */
    addExample(value) {
        const { _property } = this;
        if (!_property) {
            return;
        }
        const adapted = this._getAdapted(_property);
        const schema = adapted.schema;
        if (!schema.examples) {
            schema.examples = [];
        }
        const list = schema.examples;
        list.push(value);
        this.notifyChange();
        this.requestUpdate();
    }
    /**
     * Removes an example value at position.
     * @param index The position from which to remove the example value.
     */
    removeExample(index) {
        const { _property } = this;
        if (!_property) {
            return;
        }
        const adapted = this._getAdapted(_property);
        const schema = adapted.schema;
        if (!schema.examples) {
            return;
        }
        schema.examples.splice(index, 1);
        this.notifyChange();
        this.requestUpdate();
    }
    exampleAddHandler(e) {
        const input = e.currentTarget.parentElement;
        const { value } = input;
        if (!value) {
            return;
        }
        this.addExample(value);
        input.value = '';
    }
    exampleRemoveHandler(e) {
        const node = e.currentTarget;
        const { dataset } = node;
        if (!dataset.index) {
            return;
        }
        const index = Number(dataset.index);
        this.removeExample(index);
    }
    /**
     * Adds an enum value to the current property's schema.
     * @param value The enum value to add
     */
    addEnum(value) {
        const { _property } = this;
        if (!_property) {
            return;
        }
        const adapted = this._getAdapted(_property);
        const schema = adapted.schema;
        if (!schema.enum) {
            schema.enum = [];
        }
        const list = schema.enum;
        list.push(value);
        this.notifyChange();
        this.requestUpdate();
    }
    /**
     * Removes an enum value at position.
     * @param index The position from which to remove the enum value.
     */
    removeEnum(index) {
        const { _property } = this;
        if (!_property) {
            return;
        }
        const adapted = this._getAdapted(_property);
        const schema = adapted.schema;
        if (!schema.enum) {
            return;
        }
        schema.enum.splice(index, 1);
        this.notifyChange();
        this.requestUpdate();
    }
    enumAddHandler(e) {
        const input = e.currentTarget.parentElement;
        const { value } = input;
        if (!value) {
            return;
        }
        this.addEnum(value);
        input.value = '';
    }
    enumRemoveHandler(e) {
        const node = e.currentTarget;
        const { dataset } = node;
        if (!dataset.index) {
            return;
        }
        const index = Number(dataset.index);
        this.removeEnum(index);
    }
    _getAdapted(dataProperty) {
        let adapted;
        if (!dataProperty.adapts) {
            adapted = dataProperty.createAdapted();
        }
        else {
            adapted = dataProperty.readAdapted();
        }
        if (!adapted.schema) {
            adapted.schema = {};
        }
        return adapted;
    }
    render() {
        const { _property: item } = this;
        if (!item) {
            return html ``;
        }
        const { info, multiple = false, required = false, primary = false, index = false, readOnly = false, writeOnly = false, deprecated = false } = item;
        // const allTags = this.root?.definitions.tags;
        return html `
    <form name="data-property" @submit="${EventUtils.cancelEvent}" data-key="${item.key}">
      <ui-text-field class="input" name="name" .value="${live(info.name || '')}" label="Property name" required @change="${this._infoChangeHandler}"></ui-text-field>
      <ui-text-field class="input" name="displayName" .value="${live(info.displayName || '')}" label="Display name (optional)" @change="${this._infoChangeHandler}"></ui-text-field>
      <ui-text-field class="input" name="description" .value="${live(info.description || '')}" label="Property description (optional)" @change="${this._infoChangeHandler}"></ui-text-field>
      ${this.renderTypeSelector(item)}

      <div class="checkbox-group">
        <label class="label-large">
          <ui-checkbox name="required" .checked="${required}" title="Whether the property is required in the schema" @change="${this._checkedHandler}"></ui-checkbox>
          Required
        </label>
        <label class="label-large">
          <ui-checkbox name="multiple" .checked="${multiple}" title="When set it declares this property as an array. Multiple instances of the value are permitted." @change="${this._checkedHandler}"></ui-checkbox>
          Multiple
        </label>
        <label class="label-large">
          <ui-checkbox name="primary" .checked="${primary}" title="Makes this property a primary key for the schema. This is optional for a schema." @change="${this._checkedHandler}"></ui-checkbox>
          Primary key
        </label>
        <label class="label-large">
          <ui-checkbox name="index" .checked="${index}" title="Indicates the property is a key and should be indexed." @change="${this._checkedHandler}"></ui-checkbox>
          Indexed key
        </label>
        <label class="label-large">
          <ui-checkbox name="readOnly" .checked="${readOnly}" title="The value cannot be changed." @change="${this._checkedHandler}"></ui-checkbox>
          Read only
        </label>
        <label class="label-large">
          <ui-checkbox name="writeOnly" .checked="${writeOnly}" title="The value cannot be read." @change="${this._checkedHandler}"></ui-checkbox>
          Write only
        </label>
        <label class="label-large">
          <ui-checkbox name="deprecated" .checked="${deprecated}" title="Makes this property deprecated." @change="${this._checkedHandler}"></ui-checkbox>
          Deprecated
        </label>
      </div>
      
      ${this.renderSchemaEditor(item)}
    </form>
    `;
    }
    renderSchemaEditor(item) {
        if (['binary'].includes(item.type)) {
            return nothing;
        }
        const adapted = item.readAdapted();
        const schema = adapted && adapted.schema;
        let examples;
        let values;
        if (schema) {
            examples = schema.examples;
            values = schema.enum;
        }
        return html `
    <ui-divider type="full"></ui-divider>
    <div class="editor-title title-medium">Schema</div>
    ${this.renderDefaultValueInput(item)}
    ${this.renderExamplesEditor(item, examples)}
    ${this.renderEnumEditor(item, values)}
    `;
    }
    renderDefaultValueInput(item) {
        const adapted = item.readAdapted();
        const schema = adapted && adapted.schema;
        return this.renderTypedInput({
            name: 'defaultValue',
            label: 'Enter default value (optional)',
            type: item.type,
            handler: this.defaultValueHandler,
            value: schema?.defaultValue,
        });
    }
    renderExamplesEditor(item, examples) {
        if (['nil', 'boolean', 'binary'].includes(item.type)) {
            return nothing;
        }
        return html `
    <ui-divider type="middle"></ui-divider>
    <div class="editor-title title-small">Examples</div>
    ${this.renderExampleInput(item)}
    ${this.renderExampleList(examples)}
    `;
    }
    renderExampleInput(item) {
        return this.renderTypedInput({
            name: 'example',
            label: 'Enter example value',
            type: item.type,
            handler: this.exampleAddHandler,
            addButton: true,
        });
    }
    renderExampleList(examples) {
        if (!examples || !examples.length) {
            return html `<div class="no-description">No examples.</div>`;
        }
        return html `
    <ul class="value-list">
      ${examples.map((label, index) => html `
        <li>
          <span class="label-medium value" title="${label}">${label}</span>
          <ui-icon-button aria-label="Remove example" title="Removes the example" data-index="${index}" @click="${this.exampleRemoveHandler}">
            <ui-icon icon="deleteOutline" role="presentation"></ui-icon>
          </ui-icon-button>
        </li>
      `)}
    </ul>
    `;
    }
    renderEnumEditor(item, values) {
        if (['nil', 'boolean', 'binary'].includes(item.type)) {
            return nothing;
        }
        return html `
    <ui-divider type="middle"></ui-divider>
    <div class="editor-title title-small">Enum values</div>
    ${this.renderEnumInput(item)}
    ${this.renderEnumList(values)}
    `;
    }
    renderEnumInput(item) {
        return this.renderTypedInput({
            name: 'enum',
            label: 'Enter enum value',
            type: item.type,
            handler: this.enumAddHandler,
            addButton: true,
        });
    }
    renderEnumList(values) {
        if (!values || !values.length) {
            return html `
      <div class="no-description">No enum values.</div>
      `;
        }
        return html `
    <ul class="value-list">
      ${values.map((label, index) => html `
        <li>
          <span class="label-medium value" title="${label}">${label}</span>
          <ui-icon-button aria-label="Remove enum value" title="Removes the enum value" data-index="${index}" @click="${this.enumRemoveHandler}">
            <ui-icon icon="deleteOutline" role="presentation"></ui-icon>
          </ui-icon-button>
        </li>
      `)}
    </ul>
    `;
    }
    propertyToInputType(type) {
        switch (type) {
            // for `number` type we use text with `inputmode=numeric` attribute to mitigate the use of `step`.
            case 'integer': return 'number';
            case 'date': return 'date';
            case 'time': return 'time';
            case 'datetime': return 'datetime-local';
            default: return 'text';
        }
    }
    /**
     * Renders an input that depends on the property type.
     */
    renderTypedInput(opts) {
        if (opts.type === 'boolean') {
            return this.renderBooleanInput(opts);
        }
        if (opts.type === 'nil') {
            return this.renderNilInput(opts);
        }
        return this.renderTextField(opts);
    }
    renderTextField(opts) {
        const inputType = this.propertyToInputType(opts.type);
        let step;
        let inputMode;
        let pattern;
        if (['time', 'datetime-local'].includes(opts.type) || opts.type === 'integer') {
            step = '1';
        }
        else if (opts.type === 'number') {
            inputMode = 'numeric';
            pattern = '^\\d*\\.?\\d*$';
        }
        const changeListener = opts.addButton ? undefined : opts.handler;
        return html `
    <ui-text-field 
      class="input" 
      name="${opts.name}" 
      .value="${live(opts.value || '')}" 
      label="${opts.label}" 
      type="${inputType}" 
      step="${ifDefined(step)}"
      inputMode="${ifDefined(inputMode)}"
      pattern="${ifDefined(pattern)}"
      @change="${changeListener}"
    >
      ${opts.addButton ? html `
      <ui-icon-button type="outlined" slot="suffix" aria-label="Add value" title="Click this button to accept the value" @click="${opts.handler}">
        <ui-icon icon="add" role="presentation"></ui-icon>
      </ui-icon-button>
      ` : ''}
    </ui-text-field>
    `;
    }
    renderNilInput(opts) {
        return html `
    <ui-dropdown-list class="input dropdown" @select="${opts.handler}" closeOnOutsideClick>
      <ui-text-field readOnly label="${opts.label}" name="${opts.name}" .value="${opts.value || ''}" role="button">
        <ui-icon slot="suffix" icon="arrowDropDown"></ui-icon>
      </ui-text-field>
      <ui-list slot="dropdown" role="menu">
        <ui-list-item data-value="" role="menuitem">None</ui-list-item>
        <ui-list-item data-value="null" role="menuitem">Nil</ui-list-item>
      </ui-list>
    </ui-dropdown-list>
    `;
    }
    renderBooleanInput(opts) {
        return html `
    <ui-dropdown-list class="input dropdown" @select="${opts.handler}" closeOnOutsideClick>
      <ui-text-field readOnly label="${opts.label}" name="${opts.name}" .value="${opts.value || ''}" role="button">
        <ui-icon slot="suffix" icon="arrowDropDown"></ui-icon>
      </ui-text-field>
      <ui-list slot="dropdown" role="menu">
        <ui-list-item data-value="" role="menuitem">None</ui-list-item>
        <ui-list-item data-value="true" role="menuitem">True</ui-list-item>
        <ui-list-item data-value="false" role="menuitem">False</ui-list-item>
      </ui-list>
    </ui-dropdown-list>
    `;
    }
    renderTypeSelector(item) {
        const { type = 'any' } = item;
        return html `
    <ui-dropdown-list class="input dropdown" @select="${this.typeSelectHandler}" closeOnOutsideClick>
      <ui-text-field readOnly title="Property data type" label="Data type" .value="${type}" role="button">
        <ui-icon slot="suffix" icon="arrowDropDown"></ui-icon>
      </ui-text-field>
      <ui-list slot="dropdown" role="menu">
        ${DataPropertyTypes.map(value => html `<ui-list-item data-value="${value}" role="menuitem">${value}</ui-list-item>`)}
      </ui-list>
    </ui-dropdown-list>
    `;
    }
}
__decorate([
    property({ type: String, reflect: true })
], PropertyFormElement.prototype, "key", void 0);
__decorate([
    property({ type: Object })
], PropertyFormElement.prototype, "root", void 0);
__decorate([
    state()
], PropertyFormElement.prototype, "_property", void 0);
//# sourceMappingURL=PropertyFormElement.js.map