import { __esDecorate, __runInitializers } from "tslib";
import { html, nothing } from 'lit';
import { property, state } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { classMap } from 'lit/directives/class-map.js';
import { ModelingElement } from './ModelingElement.js';
import { DomainEntityKind, DomainFileKind, DomainModelKind, DomainNamespaceKind, } from '@api-client/core/browser.js';
import { Events } from '../../events/Events.js';
import { getDomainIconType } from '../lib/ui_mapping.js';
let DomainBrowser = (() => {
    let _classSuper = ModelingElement;
    let _withRecent_decorators;
    let _withRecent_initializers = [];
    let _withRecent_extraInitializers = [];
    let _reason_decorators;
    let _reason_initializers = [];
    let _reason_extraInitializers = [];
    let _allowed_decorators;
    let _allowed_initializers = [];
    let _allowed_extraInitializers = [];
    let _selectedTab_decorators;
    let _selectedTab_initializers = [];
    let _selectedTab_extraInitializers = [];
    let _hasAllowedSelection_decorators;
    let _hasAllowedSelection_initializers = [];
    let _hasAllowedSelection_extraInitializers = [];
    return class DomainBrowser extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _withRecent_decorators = [property({ type: Boolean })];
            _reason_decorators = [property({ type: String })];
            _allowed_decorators = [property({ type: Array })];
            _selectedTab_decorators = [state()];
            _hasAllowedSelection_decorators = [state()];
            __esDecorate(this, null, _withRecent_decorators, { kind: "accessor", name: "withRecent", static: false, private: false, access: { has: obj => "withRecent" in obj, get: obj => obj.withRecent, set: (obj, value) => { obj.withRecent = value; } }, metadata: _metadata }, _withRecent_initializers, _withRecent_extraInitializers);
            __esDecorate(this, null, _reason_decorators, { kind: "accessor", name: "reason", static: false, private: false, access: { has: obj => "reason" in obj, get: obj => obj.reason, set: (obj, value) => { obj.reason = value; } }, metadata: _metadata }, _reason_initializers, _reason_extraInitializers);
            __esDecorate(this, null, _allowed_decorators, { kind: "accessor", name: "allowed", static: false, private: false, access: { has: obj => "allowed" in obj, get: obj => obj.allowed, set: (obj, value) => { obj.allowed = value; } }, metadata: _metadata }, _allowed_initializers, _allowed_extraInitializers);
            __esDecorate(this, null, _selectedTab_decorators, { kind: "accessor", name: "selectedTab", static: false, private: false, access: { has: obj => "selectedTab" in obj, get: obj => obj.selectedTab, set: (obj, value) => { obj.selectedTab = value; } }, metadata: _metadata }, _selectedTab_initializers, _selectedTab_extraInitializers);
            __esDecorate(this, null, _hasAllowedSelection_decorators, { kind: "accessor", name: "hasAllowedSelection", static: false, private: false, access: { has: obj => "hasAllowedSelection" in obj, get: obj => obj.hasAllowedSelection, set: (obj, value) => { obj.hasAllowedSelection = value; } }, metadata: _metadata }, _hasAllowedSelection_initializers, _hasAllowedSelection_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #withRecent_accessor_storage = __runInitializers(this, _withRecent_initializers, false
        /**
         * The reason to browse the domain.
         */
        );
        /**
         * Whether to include recent items in the browsing.
         */
        get withRecent() { return this.#withRecent_accessor_storage; }
        set withRecent(value) { this.#withRecent_accessor_storage = value; }
        #reason_accessor_storage = (__runInitializers(this, _withRecent_extraInitializers), __runInitializers(this, _reason_initializers, 'pick'
        /**
         * Specifies which items are allowed to select in the UI to return the result
         * back to the application. By default all data domain items are allowed.
         */
        ));
        /**
         * The reason to browse the domain.
         */
        get reason() { return this.#reason_accessor_storage; }
        set reason(value) { this.#reason_accessor_storage = value; }
        #allowed_accessor_storage = (__runInitializers(this, _reason_extraInitializers), __runInitializers(this, _allowed_initializers, void 0));
        /**
         * Specifies which items are allowed to select in the UI to return the result
         * back to the application. By default all data domain items are allowed.
         */
        get allowed() { return this.#allowed_accessor_storage; }
        set allowed(value) { this.#allowed_accessor_storage = value; }
        #selectedTab_accessor_storage = (__runInitializers(this, _allowed_extraInitializers), __runInitializers(this, _selectedTab_initializers, 'domain'
        /**
         * Whether there is an allowed selection.
         */
        ));
        /**
         * The currently selected tab.
         * @default 'domain'
         */
        get selectedTab() { return this.#selectedTab_accessor_storage; }
        set selectedTab(value) { this.#selectedTab_accessor_storage = value; }
        #hasAllowedSelection_accessor_storage = (__runInitializers(this, _selectedTab_extraInitializers), __runInitializers(this, _hasAllowedSelection_initializers, false
        /**
         * The recent browsing partition state.
         */
        ));
        /**
         * Whether there is an allowed selection.
         */
        get hasAllowedSelection() { return this.#hasAllowedSelection_accessor_storage; }
        set hasAllowedSelection(value) { this.#hasAllowedSelection_accessor_storage = value; }
        /**
         * The recent browsing partition state.
         */
        recent = (__runInitializers(this, _hasAllowedSelection_extraInitializers), {
            currentList: [],
        });
        /**
         * The current data domain partition state.
         */
        current;
        /**
         * Returns the active browsing partition based on the selected tab.
         */
        get activePartition() {
            if (this.selectedTab === 'recent') {
                return this.recent;
            }
            if (!this.current) {
                throw new Error('Browsing domain partition is not set.');
            }
            return this.current;
        }
        willUpdate(cp) {
            super.willUpdate(cp);
            if (cp.has('root')) {
                this.setupCurrentPartition();
                this.setupRecent();
            }
            if (cp.has('withRecent')) {
                this.setupRecent();
            }
        }
        setupCurrentPartition() {
            const { root } = this;
            if (!root) {
                this.current = undefined;
                this.hasAllowedSelection = false;
                return;
            }
            this.current = {
                items: [],
            };
            this.setupAllowedSelection(this.current);
            this.updateBrowsingDomainList();
        }
        /**
         * Sets up whether a selection is allowed based on the current state.
         * @param partition - The partition to check.
         */
        setupAllowedSelection(partition) {
            const { allowed = [] } = this;
            const { selected } = partition;
            if (!Array.isArray(allowed) || allowed.length === 0) {
                if (selected) {
                    this.hasAllowedSelection = true;
                }
                return;
            }
            if (!selected) {
                this.hasAllowedSelection = false;
                return;
            }
            const { kind } = selected;
            if (!kind) {
                this.hasAllowedSelection = false;
                return;
            }
            this.hasAllowedSelection = allowed.some((item) => {
                if (item === DomainEntityKind) {
                    return kind === DomainEntityKind;
                }
                if (item === DomainModelKind) {
                    return kind === DomainModelKind;
                }
                if (item === DomainNamespaceKind) {
                    return kind === DomainNamespaceKind || kind === DomainFileKind;
                }
                return false;
            });
        }
        /**
         * Updates the current list of items in a browsing domain partition.
         * @param partition - The partition to update.
         */
        updateBrowsingDomainList() {
            const { root, current } = this;
            if (!current || !root) {
                return;
            }
            current.items = [];
            const { parent } = current;
            if (!parent) {
                current.items = [...root.listNamespaces(), ...root.listModels()];
                if (root.dependencies.size > 0) {
                    for (const foreign of root.dependencies.values()) {
                        current.items.push(...root.listForeignNamespaces(foreign.key));
                        current.items.push(...root.listForeignModels(foreign.key));
                    }
                }
            }
            else if (parent.kind === DomainModelKind) {
                const model = root.findModel(parent.key, parent.namespace);
                if (model) {
                    current.items = [...model.listEntities()];
                }
            }
            else if (parent.kind === DomainNamespaceKind) {
                const namespace = root.findNamespace(parent.key, parent.namespace);
                if (namespace) {
                    current.items = [...namespace.listNamespaces(), ...namespace.listModels()];
                }
            }
            this.requestUpdate();
        }
        /**
         * Requests the recent items from the context provider if `withRecent` is enabled.
         */
        async setupRecent() {
            if (!this.withRecent) {
                this.recent.currentList = [];
                if (this.selectedTab === 'recent') {
                    this.selectedTab = 'domain';
                }
                return;
            }
            const { root } = this;
            if (!root) {
                this.recent.currentList = [];
                return;
            }
            const result = await Events.Domain.Browse.recent(root.key, this.allowed, this);
            this.recent.currentList = result ?? [];
        }
        /**
         * Checks if an item is selected.
         * @param key The key of the item to check.
         * @returns True if the item is selected, false otherwise.
         */
        isSelected(key) {
            const browsing = this.activePartition;
            if (!browsing.selected) {
                return false;
            }
            return browsing.selected.key === key;
        }
        /**
         * Handles the tab selection event.
         * @param e - The tab selection event.
         */
        handleTabSelect(e) {
            const { item } = e.detail;
            const { key } = item.dataset;
            if (!key) {
                return;
            }
            this.selectedTab = key;
            this.requestUpdate();
        }
        /**
         * Handles the click event to go back to the parent.
         */
        handleParentBack() {
            const source = this.current;
            if (!source || !source.parent) {
                return;
            }
            if (this.selectedTab === 'domain') {
                const domainElement = source.parent;
                const ns = domainElement.getParentInstance();
                if (!ns || ns.key === ns.domain.key) {
                    source.parent = undefined;
                }
                else {
                    this.selectParent(source, ns.key, ns.kind, ns.domain.key);
                }
                this.updateBrowsingDomainList();
            }
        }
        /**
         * Selects a parent item.
         *
         * @param partition - The partition to select the parent in.
         * @param key - The key of the parent item.
         * @param kind - The kind of the parent item.
         * @throws {Error} If the root is not set or the item is not found.
         */
        selectParent(partition, key, kind, namespace) {
            const { root } = this;
            if (!root) {
                throw new Error(`Unable to select parent. The root is not set.`);
            }
            if (kind === DomainModelKind) {
                partition.parent = root.findModel(key, namespace);
            }
            else if (kind === DomainNamespaceKind) {
                partition.parent = root.findNamespace(key, namespace);
            }
            if (!partition.parent) {
                throw new Error(`Unable to select parent. The item with key "${key}" was not found.`);
            }
        }
        /**
         * Handles the click event to open a parent item.
         *
         * @param e - The click event.
         */
        handleOpenParent(e) {
            e.preventDefault();
            e.stopPropagation();
            const button = e.currentTarget;
            const item = button.parentElement;
            const { kind, key, parent, namespace } = item.dataset;
            if (!key || !kind || !parent || parent !== 'true' || !this.current) {
                return;
            }
            this.selectParent(this.current, key, kind, namespace);
            this.updateBrowsingDomainList();
        }
        /**
         * Handles the selection event on a list.
         * @param e The selection event.
         */
        handleItemSelect(e) {
            const { item } = e.detail;
            if (!item) {
                return;
            }
            const { kind, key, parent, namespace } = item.dataset;
            if (!key || !kind || !parent || !namespace) {
                return;
            }
            const state = this.activePartition;
            if (this.isSelected(key)) {
                state.selected = undefined;
            }
            else {
                state.selected = {
                    key,
                    kind,
                    isParent: parent === 'true',
                    namespace,
                };
            }
            this.setupAllowedSelection(state);
            this.requestUpdate();
            if (this.hasAllowedSelection) {
                // We only notify the selection if it is allowed.
                this.notifySelected(state.selected);
            }
        }
        notifySelected(selected) {
            this.dispatchEvent(new CustomEvent('select', { detail: selected, bubbles: false, composed: false }));
        }
        render() {
            if (!this.root) {
                return nothing;
            }
            const result = [];
            result.push(this.renderHeader());
            if (this.selectedTab === 'recent') {
                result.push(this.renderRecent());
            }
            else if (this.selectedTab === 'domain') {
                result.push(this.renderBrowsingDomain());
            }
            return html `${result}`;
        }
        /**
         * Renders the header of the dialog.
         * @private
         */
        renderHeader() {
            if (this.selectedTab === 'recent') {
                return this.renderTabs();
            }
            const activePartition = this.current;
            if (!activePartition) {
                return nothing;
            }
            const { parent } = activePartition;
            if (parent) {
                return this.renderParentHeader(parent);
            }
            return this.renderTabs();
        }
        /**
         * Renders the tabs.
         * @private
         */
        renderTabs() {
            const { withRecent, selectedTab, root } = this;
            return html `<ui-tabs @change="${this.handleTabSelect}" class="tabs">
      ${withRecent
                ? html `<ui-tab aria-controls="recent" data-key="recent" ?selected="${selectedTab === 'recent'}">Recent</ui-tab>`
                : nothing}
      ${root?.key
                ? html `<ui-tab aria-controls="domain" data-key="domain" ?selected="${selectedTab === 'domain'}"
            >Data domain</ui-tab
          >`
                : nothing}
    </ui-tabs>`;
        }
        /**
         * Renders the parent header.
         * @private
         * @param parent - The parent item.
         */
        renderParentHeader(parent) {
            const label = parent.info.displayName || parent.info.name;
            return html `<div class="parent-header">
        <ui-icon-button @click="${this.handleParentBack}"><ui-icon>arrow_back</ui-icon></ui-icon-button>
        <h2 class="title-medium">${label}</h2>
      </div>
      <ui-divider></ui-divider>`;
        }
        /**
         * Renders the recent items.
         * @private
         */
        renderRecent() {
            const { recent } = this;
            return html `<div id="recent">${this.renderRecentItems(recent.currentList)}</div>`;
        }
        /**
         * Renders the browsing domain items.
         * @private
         */
        renderBrowsingDomain() {
            const { current } = this;
            if (!current) {
                return nothing;
            }
            return html `<div id="domain">${this.renderDomainItems(current.items)}</div>`;
        }
        /**
         * Renders the domain items.
         * @private
         * @param items - The items to render.
         */
        renderDomainItems(items) {
            const empty = items.length === 0;
            if (empty) {
                return html `<p class="empty-list body-medium">No items found.</p>`;
            }
            return html `<ui-list selectActive class="domain-list" @select="${this.handleItemSelect}">
      ${items.map((item) => this.renderDomainItem(item))}
    </ui-list>`;
        }
        renderRecentItems(items) {
            const empty = items.length === 0;
            if (empty) {
                return html `<p class="empty-list body-medium">No items found.</p>`;
            }
            return html `<ui-list selectActive class="domain-list" @select="${this.handleItemSelect}">
      ${items.map((item) => this.renderRecentItem(item))}
    </ui-list>`;
        }
        renderDomainItem(item) {
            const isParent = [DomainModelKind, DomainNamespaceKind].includes(item.kind);
            return this.renderItem(item.key, item.kind, item.info.getLabel(), { isParent, domain: item.namespace });
        }
        renderRecentItem(item) {
            const { key, kind, label } = item;
            return this.renderItem(key, kind, label, { domain: this.root?.key });
        }
        /**
         * Renders a generic item.
         * @private
         * @param key - The key of the item.
         * @param kind - The kind of the item.
         * @param label - The label of the item.
         * @param {{ noParent?: boolean }} opts - Additional options.
         */
        renderItem(key, kind, label, opts = {}) {
            const isSelected = this.isSelected(key);
            const parentValue = opts.isParent ? 'true' : 'false';
            const classes = {
                select: isSelected,
            };
            const icon = getDomainIconType(kind);
            return html `<ui-list-item
      data-kind="${kind}"
      data-key="${key}"
      data-parent="${parentValue}"
      data-namespace="${ifDefined(opts.domain)}"
      class="${classMap(classes)}"
    >
      <ui-icon slot="start" class="icon" icon="${icon}"></ui-icon>
      <span>${label}</span>
      ${isSelected && opts.isParent
                ? html `<ui-button color="filled" slot="end" @click="${this.handleOpenParent}">Open</ui-button>`
                : nothing}
    </ui-list-item>`;
        }
    };
})();
/**
 * An element that provides browsing capabilities for a data domain.
 *
 * @fires select - Fired when a domain item is selected or deselected.
 *   The detail contains `DomainBrowseSelectedItem` with the selected item's key, kind,
 *   namespace, and whether it's a parent (can be opened), or `undefined` if deselected.
 */
export default DomainBrowser;
//# sourceMappingURL=DomainBrowser.js.map