import { __esDecorate, __runInitializers } from "tslib";
import { html, nothing } from 'lit';
import { property, query } from 'lit/decorators.js';
import { SnackNotifications } from '../../md/notification/SnackNotifications.js';
import { HttpFlowsUi } from './HttpFlowsUi.js';
import '../../md/list/ui-list.js';
import '../../md/list/ui-list-item.js';
import '../../md/dropdown-list/ui-dropdown-list.js';
import '../../md/button/ui-button.js';
import '../../md/divider/ui-divider.js';
import '../../md/dialog/ui-dialog.js';
import '../dialog/rename-dialog.js';
let HttpFlows = (() => {
    let _classSuper = HttpFlowsUi;
    let _model_decorators;
    let _model_initializers = [];
    let _model_extraInitializers = [];
    let _trigger_decorators;
    let _trigger_initializers = [];
    let _trigger_extraInitializers = [];
    let _selectedFlow_decorators;
    let _selectedFlow_initializers = [];
    let _selectedFlow_extraInitializers = [];
    let _flowAddDialog_decorators;
    let _flowAddDialog_initializers = [];
    let _flowAddDialog_extraInitializers = [];
    return class HttpFlows extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _model_decorators = [property({ type: Array })];
            _trigger_decorators = [property({ type: String })];
            _selectedFlow_decorators = [property({ type: Number })];
            _flowAddDialog_decorators = [query('#createFlowDialog')];
            __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, _trigger_decorators, { kind: "accessor", name: "trigger", static: false, private: false, access: { has: obj => "trigger" in obj, get: obj => obj.trigger, set: (obj, value) => { obj.trigger = value; } }, metadata: _metadata }, _trigger_initializers, _trigger_extraInitializers);
            __esDecorate(this, null, _selectedFlow_decorators, { kind: "accessor", name: "selectedFlow", static: false, private: false, access: { has: obj => "selectedFlow" in obj, get: obj => obj.selectedFlow, set: (obj, value) => { obj.selectedFlow = value; } }, metadata: _metadata }, _selectedFlow_initializers, _selectedFlow_extraInitializers);
            __esDecorate(this, null, _flowAddDialog_decorators, { kind: "accessor", name: "flowAddDialog", static: false, private: false, access: { has: obj => "flowAddDialog" in obj, get: obj => obj.flowAddDialog, set: (obj, value) => { obj.flowAddDialog = value; } }, metadata: _metadata }, _flowAddDialog_initializers, _flowAddDialog_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #model_accessor_storage = __runInitializers(this, _model_initializers, []
        /**
         * The setting that renders flows for the given trigger (request or response).
         */
        );
        /**
         * The list of HTTP flows.
         * This is a live list. All changes are propagated in the model.
         */
        get model() { return this.#model_accessor_storage; }
        set model(value) { this.#model_accessor_storage = value; }
        #trigger_accessor_storage = (__runInitializers(this, _model_extraInitializers), __runInitializers(this, _trigger_initializers, 'response'
        /**
         * The index of the currently selected flow. This is the index on the `model` property.
         * @attribute
         */
        ));
        /**
         * The setting that renders flows for the given trigger (request or response).
         */
        get trigger() { return this.#trigger_accessor_storage; }
        set trigger(value) { this.#trigger_accessor_storage = value; }
        #selectedFlow_accessor_storage = (__runInitializers(this, _trigger_extraInitializers), __runInitializers(this, _selectedFlow_initializers, void 0));
        /**
         * The index of the currently selected flow. This is the index on the `model` property.
         * @attribute
         */
        get selectedFlow() { return this.#selectedFlow_accessor_storage; }
        set selectedFlow(value) { this.#selectedFlow_accessor_storage = value; }
        #flowAddDialog_accessor_storage = (__runInitializers(this, _selectedFlow_extraInitializers), __runInitializers(this, _flowAddDialog_initializers, null
        /**
         * The currently selected flow definition.
         *
         * If there's no selection it returns the first flow.
         */
        ));
        get flowAddDialog() { return this.#flowAddDialog_accessor_storage; }
        set flowAddDialog(value) { this.#flowAddDialog_accessor_storage = value; }
        /**
         * The currently selected flow definition.
         *
         * If there's no selection it returns the first flow.
         */
        get currentFlow() {
            const { model = [], selectedFlow, trigger = 'response' } = this;
            if (selectedFlow === undefined) {
                return undefined;
            }
            const item = model[selectedFlow];
            if (!item || item.trigger !== trigger) {
                return undefined;
            }
            return item;
        }
        connectedCallback() {
            super.connectedCallback();
            this.preSelectFlow();
        }
        willUpdate(cp) {
            if (cp.has('trigger')) {
                this.selectedFlow = undefined;
                this.preSelectFlow();
            }
            super.willUpdate(cp);
        }
        preSelectFlow() {
            const { model = [], trigger = 'response', selectedFlow } = this;
            if (Number.isInteger(selectedFlow)) {
                return;
            }
            const index = model.findIndex((i) => i.trigger === trigger);
            if (index >= 0) {
                this.selectedFlow = index;
            }
        }
        addFlow(description) {
            const flow = {
                trigger: this.trigger,
                actions: [],
            };
            if (description) {
                flow.description = description;
            }
            const len = this.model.push(flow);
            this.selectedFlow = len - 1;
            this.notifyChange();
            this.requestUpdate();
        }
        removeFlow(index) {
            const { model } = this;
            if (index >= model.length || index < 0) {
                throw RangeError(`Invalid index: ${index}. Expected an index in range from 0 to ${model.length - 1}`);
            }
            const removed = model[index];
            model.splice(index, 1);
            this.requestUpdate();
            this.notifyChange();
            if (!model[index]) {
                this.selectedFlow = 0;
            }
            SnackNotifications.notify(`Flow was deleted.`, {
                actionLabel: 'Redo',
                actionCallback: () => {
                    model.splice(index, 0, removed);
                    this.selectedFlow = index;
                    this.requestUpdate();
                    this.notifyChange();
                },
                cancellable: true,
            });
        }
        removeFlowAction(actionIndex) {
            const { currentFlow } = this;
            if (!currentFlow) {
                return;
            }
            currentFlow.actions.splice(actionIndex, 1);
            this.notifyChange();
            this.requestUpdate();
        }
        renameFlowAction(actionIndex) {
            const { currentFlow } = this;
            if (!currentFlow) {
                return;
            }
            const action = currentFlow.actions[actionIndex];
            if (!action) {
                return;
            }
            const dialog = document.createElement('rename-dialog');
            dialog.name = action.description;
            dialog.modal = true;
            document.body.appendChild(dialog);
            dialog.open = true;
            dialog.addEventListener('close', (ev) => {
                document.parentElement?.removeChild(dialog);
                const event = ev;
                const { cancelled, value } = event.detail;
                if (!cancelled && value) {
                    action.description = value;
                    this.notifyChange();
                    this.requestUpdate();
                }
            });
        }
        handleAdd() {
            const { flowAddDialog } = this;
            if (!flowAddDialog) {
                return;
            }
            flowAddDialog.open = true;
        }
        handleTriggerSelect(e) {
            const target = e.detail.item;
            if (!target) {
                return;
            }
            const { trigger } = target.dataset;
            if (!trigger) {
                return;
            }
            this.trigger = trigger;
        }
        handleFlowSelect(e) {
            const target = e.detail.item;
            if (!target) {
                return;
            }
            const { trigger = 'response', model = [] } = this;
            const option = target.dataset.option;
            if (option === 'create') {
                this.handleAdd();
            }
            else if (option === 'delete') {
                const { currentFlow } = this;
                if (!currentFlow) {
                    return;
                }
                const index = model.indexOf(currentFlow);
                if (index >= 0) {
                    this.removeFlow(index);
                }
            }
            else if (option === 'select') {
                const index = Number(target.dataset.index);
                if (Number.isNaN(index)) {
                    return;
                }
                let current = -1;
                for (let i = 0, len = model.length; i < len; i++) {
                    const item = model[i];
                    if (item.trigger !== trigger) {
                        continue;
                    }
                    current += 1;
                    if (current === index) {
                        this.selectedFlow = i;
                        break;
                    }
                }
            }
        }
        handleFlowCreateDialogResult(e) {
            if (e.detail.cancelled) {
                return;
            }
            const dialog = e.target;
            const input = dialog.querySelector('ui-filled-text-field');
            if (!input) {
                return;
            }
            const { value } = input;
            input.value = '';
            this.addFlow(value);
        }
        /**
         * On the editor element we only allow "flow blocks" items (conditions and actions).
         * Only one condition can be added so once a condition is set we do not allow to add another condition.
         */
        handleEditorDragOver(e) {
            const { dataTransfer } = e;
            if (e.defaultPrevented || !dataTransfer || !this.isValidDropTarget(dataTransfer)) {
                return;
            }
            e.preventDefault();
            dataTransfer.dropEffect = 'copy';
            const target = e.currentTarget;
            target.classList.add('dragover');
        }
        handleEditorDragLeave(e) {
            if (e.defaultPrevented || !this.isValidDropTarget(e.dataTransfer)) {
                return;
            }
            const target = e.currentTarget;
            target.classList.remove('dragover');
        }
        handleEditorDrop(e) {
            const { dataTransfer } = e;
            if (!dataTransfer || e.defaultPrevented || !this.isValidDropTarget(dataTransfer)) {
                return;
            }
            e.preventDefault();
            this.resetDragIndices();
            const group = dataTransfer.getData('flow/group');
            if (group !== 'flow-block') {
                SnackNotifications.notify('Only blocks from the "Flow blocks" group can be dropped here.', 2000);
                return;
            }
            const kind = dataTransfer.getData('flow/kind');
            if (kind === 'Core#Condition') {
                this.setCondition();
            }
            else if (kind === 'Core#Action') {
                this.addAction();
            }
        }
        /**
         * Adds a condition to the current flow.
         * Note, when the condition already exists this does nothing.
         */
        setCondition() {
            const { currentFlow } = this;
            if (!currentFlow) {
                SnackNotifications.notify('Select a flow first.', 2000);
                return;
            }
            if (currentFlow.condition) {
                SnackNotifications.notify('This flow already have a condition.', 2000);
                return;
            }
            currentFlow.condition = {
                steps: [],
            };
            this.requestUpdate();
            this.notifyChange();
        }
        addAction() {
            const { currentFlow } = this;
            if (!currentFlow) {
                SnackNotifications.notify('Select a flow first.', 2000);
                return;
            }
            if (!Array.isArray(currentFlow.actions)) {
                currentFlow.actions = [];
            }
            currentFlow.actions.push({
                steps: [],
            });
            this.requestUpdate();
            this.notifyChange();
        }
        stepFromChangeEvent(e) {
            const input = e.target;
            input.checkValidity();
            const { actionIndex: aci, assertionIndex: asi } = input.dataset;
            const actionIndex = Number(aci || asi);
            const action = this.getStepsTarget(actionIndex);
            if (!action) {
                return undefined;
            }
            const stepIndex = Number(input.dataset.stepIndex);
            if (Number.isNaN(stepIndex)) {
                return undefined;
            }
            return action.steps[stepIndex];
        }
        getStepsTarget(assertionOrActionIndex) {
            if (Number.isNaN(assertionOrActionIndex)) {
                return undefined;
            }
            const { currentFlow } = this;
            if (!currentFlow) {
                return undefined;
            }
            if (assertionOrActionIndex === -1) {
                return currentFlow.condition;
            }
            return currentFlow.actions[assertionOrActionIndex];
        }
        handleConditionOption(e) {
            const { currentFlow } = this;
            if (!currentFlow) {
                return;
            }
            const { item } = e.detail;
            const { option } = item.dataset;
            if (option === 'delete') {
                delete currentFlow.condition;
                this.notifyChange();
                this.requestUpdate();
            }
        }
        handleActionOption(e) {
            const list = e.target;
            const actionIndex = Number(list.dataset.actionIndex);
            if (Number.isNaN(actionIndex)) {
                return;
            }
            const { item } = e.detail;
            const { option } = item.dataset;
            switch (option) {
                case 'delete':
                    this.removeFlowAction(actionIndex);
                    break;
                case 'rename':
                    this.renameFlowAction(actionIndex);
                    break;
                default:
            }
        }
        isConditionTarget(e) {
            const path = e.composedPath();
            return path.some((i) => {
                const element = i;
                return !!element.classList && element.classList.contains('condition-row');
            });
        }
        /**
         * We override this method tpo handle conditions which can only accept certain
         * blocks and have index of `-1`
         */
        handleTargetDragOver(e) {
            if (e.defaultPrevented) {
                return;
            }
            const dt = e.dataTransfer;
            if (!this.isConditionTarget(e)) {
                if (dt.types.includes('flow/assertion')) {
                    // we do not allow assertions in actions.
                    e.preventDefault();
                    return;
                }
                super.handleTargetDragOver(e);
                return;
            }
            if (dt.types.includes('flow/action')) {
                e.preventDefault();
                dt.dropEffect = 'none';
                return;
            }
            super.handleTargetDragOver(e);
        }
        /**
         * Overrides the method to support conditions.
         */
        handleDrop(e) {
            if (e.defaultPrevented) {
                return;
            }
            const dt = e.dataTransfer;
            if (!this.isConditionTarget(e)) {
                if (dt.types.includes('flow/assertion')) {
                    // we do not allow assertions in actions.
                    e.preventDefault();
                    SnackNotifications.notify(`Assertions cannot be a part of a flow action.`, 3000);
                    return;
                }
                super.handleDrop(e);
                return;
            }
            if (dt.types.includes('flow/action')) {
                e.preventDefault();
                SnackNotifications.notify(`Flow actions cannot be a part of a condition.`, 3000);
                return;
            }
            super.handleDrop(e);
        }
        render() {
            return html `
      ${this.renderActionButtons()}
      <div class="container">
        ${this.renderEditor()}
        <ui-divider vertical></ui-divider>
        ${this.renderBuildingBlocks()}
      </div>
      ${this.renderCreateFlowDialog()}
    `;
        }
        renderActionButtons() {
            return html ` <div class="editor-actions">${this.renderTriggerSelector()} ${this.renderFlowSelector()}</div> `;
        }
        renderTriggerSelector() {
            const { trigger = 'response' } = this;
            const label = trigger === 'request' ? 'Pre-request' : 'Post-request';
            return html `
      <ui-dropdown-list @select="${this.handleTriggerSelect}" horizontalAlign="right" closeOnOutsideClick>
        <ui-button color="outlined" class="new-button">
          <ui-icon icon="arrowDropDown" slot="icon"></ui-icon>
          ${label}
        </ui-button>
        <ui-list slot="dropdown" role="menu">
          <ui-list-item data-trigger="request" role="menuitem" title="Select pre-request flows"
            >Pre-request</ui-list-item
          >
          <ui-list-item data-trigger="response" role="menuitem" title="Select post-request flows"
            >Post-request</ui-list-item
          >
        </ui-list>
      </ui-dropdown-list>
    `;
        }
        renderFlowSelector() {
            const { trigger = 'response', currentFlow, model } = this;
            const label = currentFlow ? currentFlow.description || 'Unnamed flow' : 'Select a flow';
            const items = model.filter((i) => i.trigger === trigger);
            const hasItems = !!items.length;
            return html `
      <ui-dropdown-list @select="${this.handleFlowSelect}" horizontalAlign="right" closeOnOutsideClick>
        <ui-button color="outlined" class="new-button">
          <ui-icon icon="arrowDropDown" slot="icon"></ui-icon>
          ${label}
        </ui-button>
        <ui-list slot="dropdown" role="menu">
          ${hasItems
                ? html `
                ${items.map((item, index) => html `<ui-list-item
                      data-option="select"
                      data-index="${index}"
                      role="menuitem"
                      title="Select this flow"
                      >${item.description || `Flow # ${index}`}</ui-list-item
                    >`)}
                <ui-divider></ui-divider>
              `
                : nothing}
          ${currentFlow
                ? html ` <ui-list-item image="icon" data-option="delete" role="menuitem" title="Deletes the current flow">
                <ui-icon icon="deleteOutline" slot="start"></ui-icon>
                Delete
              </ui-list-item>`
                : nothing}
          <ui-list-item image="icon" data-option="create" role="menuitem" title="Adds a new flow">
            <ui-icon icon="add" slot="start"></ui-icon>
            Create
          </ui-list-item>
        </ui-list>
      </ui-dropdown-list>
    `;
        }
        renderCreateFlowDialog() {
            return html `
      <ui-dialog
        modal
        id="createFlowDialog"
        class="add-dialog"
        @close="${this.handleFlowCreateDialogResult}"
        dismissLabel="Cancel"
        confirmLabel="Add"
      >
        <span slot="title">New Flow</span>

        <ui-filled-text-field
          class="input"
          label="Flow name"
          name="name"
          required
          invalidText="Flow name is required"
        ></ui-filled-text-field>
      </ui-dialog>
    `;
        }
        renderEditor() {
            const { currentFlow, model = [], trigger } = this;
            const content = [];
            if (!currentFlow && !model.filter((i) => i.trigger === trigger).length) {
                content.push(this.renderEmptyFlowMessage());
            }
            else if (!currentFlow) {
                content.push(this.renderSelectFlowMessage());
            }
            else {
                const { condition, actions } = currentFlow;
                if (!condition && (!actions || !actions.length)) {
                    content.push(this.renderEmptyActionsMessage());
                }
                else {
                    if (condition) {
                        content.push(this.renderCondition(condition));
                    }
                    if (actions && actions.length) {
                        content.push(this.renderList(currentFlow.actions));
                    }
                }
            }
            return html `
      <section
        aria-label="Flows editor"
        class="editor surface1"
        @dragover="${this.handleEditorDragOver}"
        @dragleave="${this.handleEditorDragLeave}"
        @drop="${this.handleEditorDrop}"
      >
        ${content}
      </section>
    `;
        }
        renderEmptyFlowMessage() {
            return html `
      <div class="empty-message">Create a flow to get started.</div>
      <div class="empty-message">
        <ui-button color="outlined" @click="${this.handleAdd}" class="empty-button">Create</ui-button>
      </div>
    `;
        }
        renderSelectFlowMessage() {
            return html ` <div class="empty-message">Select a flow to get started.</div> `;
        }
        renderEmptyActionsMessage() {
            return html ` <div class="empty-message">Drop a condition or an action here</div> `;
        }
        renderCondition(condition) {
            return html `
      <div class="step-row condition">
        <div class="action-tab">
          <span class="body-small">Condition</span>
          ${this.renderConditionTabActions()}
        </div>
        <div class="param-row condition-row">${this.renderSteps(condition, -1)}</div>
      </div>
    `;
        }
        renderList(model) {
            return html ` ${model.map((item, index) => this.renderAction(item, index))} `;
        }
        renderAction(item, index) {
            const { description } = item;
            const label = description || `Action ${index + 1}`;
            return html `
      <div class="step-row action">
        <div class="action-tab">
          <span class="body-small">${label}</span>
          ${this.renderStepTabActions(index)}
        </div>
        <div class="param-row action-row">${this.renderSteps(item, index)}</div>
      </div>
    `;
        }
        renderBuildingBlocks() {
            const { currentFlow } = this;
            return html `
      <section aria-label="Flow building block" class="options">
        ${this.renderFlowBlocksOptions(currentFlow)} ${this.renderDataSourceOptions(currentFlow)}
        ${this.renderTransformationOptions(currentFlow)} ${this.renderActions(currentFlow)}
        ${this.renderAssertionOptions(currentFlow)}
      </section>
    `;
        }
        renderFlowBlocksOptions(currentFlow) {
            const allDisabled = !currentFlow;
            const conditionDisabled = allDisabled || (!!currentFlow && !!currentFlow.condition);
            return html `
      <details class="action-group" open>
        <summary>Flow Blocks</summary>
        <div class="group-content">
          ${this.renderBlockChip('Condition', 'flow-block', 'Core#Condition', {
                disabled: conditionDisabled,
                title: 'Adds a condition that is checked before the flow is executed. Only one condition per flow.',
            })}
          ${this.renderBlockChip('Action', 'flow-block', 'Core#Action', {
                disabled: allDisabled,
                title: 'Adds an action to this flow.',
            })}
        </div>
      </details>
    `;
        }
        renderDataSourceOptions(currentFlow) {
            let allDisabled = !currentFlow;
            if (currentFlow) {
                const { condition, actions = [] } = currentFlow;
                if (!condition && !actions.length) {
                    allDisabled = true;
                }
            }
            const { trigger = 'response' } = this;
            if (trigger === 'request') {
                return this.renderRequestDataSourceSuggestions(allDisabled);
            }
            return this.renderResponseDataSourceSuggestions(allDisabled);
        }
        renderTransformationOptions(currentFlow) {
            let allDisabled = !currentFlow;
            if (currentFlow) {
                const { condition, actions = [] } = currentFlow;
                if (!condition && !actions.length) {
                    allDisabled = true;
                }
            }
            return this.renderTransformationSuggestions(allDisabled);
        }
        renderActions(currentFlow) {
            let allDisabled = !currentFlow;
            if (currentFlow) {
                const { condition, actions = [] } = currentFlow;
                if (!condition && !actions.length) {
                    allDisabled = true;
                }
            }
            return this.renderActionSuggestions(allDisabled);
        }
        renderAssertionOptions(currentFlow) {
            let allDisabled = !currentFlow;
            if (currentFlow) {
                const { condition, actions = [] } = currentFlow;
                if (!condition && !actions.length) {
                    allDisabled = true;
                }
            }
            return this.renderAssertionSuggestions(allDisabled);
        }
        renderConditionTabActions() {
            return html `
      <ui-dropdown-list @select="${this.handleConditionOption}" closeOnOutsideClick>
        <ui-icon-button class="condition-options-trigger">
          <ui-icon icon="moreVert"></ui-icon>
        </ui-icon-button>
        <ui-list slot="dropdown" role="menu">
          <ui-list-item data-option="delete" role="menuitem">Remove</ui-list-item>
        </ui-list>
      </ui-dropdown-list>
    `;
        }
        renderStepTabActions(actionIndex) {
            return html `
      <ui-dropdown-list @select="${this.handleActionOption}" closeOnOutsideClick data-action-index="${actionIndex}">
        <ui-icon-button class="action-options-trigger">
          <ui-icon icon="moreVert"></ui-icon>
        </ui-icon-button>
        <ui-list slot="dropdown" role="menu">
          <ui-list-item data-option="rename" role="menuitem">Rename</ui-list-item>
          <ui-divider></ui-divider>
          <ui-list-item data-option="delete" role="menuitem">Remove</ui-list-item>
        </ui-list>
      </ui-dropdown-list>
    `;
        }
        constructor() {
            super(...arguments);
            __runInitializers(this, _flowAddDialog_extraInitializers);
        }
    };
})();
export default HttpFlows;
//# sourceMappingURL=HttpFlows.element.js.map