var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { addClass, removeClass } from "@matdata/yasgui-utils";
import { filterFolderEntriesByName } from "./browserFilter";
import { getWorkspaceBackend } from "./backends/getWorkspaceBackend";
import { asWorkspaceBackendError } from "./backends/errors";
import { getEndpointToAutoSwitch } from "./openManagedQuery";
import { hashQueryText } from "./textHash";
import { normalizeQueryFilename } from "./normalizeQueryFilename";
import "./QueryBrowser.scss";
export default class QueryBrowser {
    entrySignature(entry) {
        const parent = entry.parentId || "";
        return `${entry.kind}:${entry.id}:${entry.label}:${parent}`;
    }
    invalidateRenderCache() {
        this.lastRenderedSignature = undefined;
    }
    invalidateAndRefresh(workspaceId) {
        if (!workspaceId || this.treeWorkspaceId === workspaceId) {
            this.folderEntriesById.clear();
            this.folderLoadingById.clear();
            this.folderErrorById.clear();
            this.queryPreviewById.clear();
            this.queryPreviewLoadingById.clear();
            this.invalidateRenderCache();
            if (this.isOpen)
                void this.refresh();
        }
    }
    constructor(yasgui) {
        this.isOpen = false;
        this.expandedFolderIds = new Set();
        this.folderEntriesById = new Map();
        this.folderLoadingById = new Set();
        this.folderErrorById = new Map();
        this.refreshRunId = 0;
        this.queryPreviewById = new Map();
        this.queryPreviewLoadingById = new Set();
        this.yasgui = yasgui;
        this.rootEl = document.createElement("div");
        addClass(this.rootEl, "yasgui-query-browser");
        this.tooltipEl = document.createElement("div");
        addClass(this.tooltipEl, "yasgui-query-browser__tooltip");
        this.tooltipEl.setAttribute("role", "tooltip");
        this.tooltipEl.setAttribute("aria-hidden", "true");
        this.drawerEl = document.createElement("div");
        addClass(this.drawerEl, "yasgui-query-browser__drawer");
        this.headerEl = document.createElement("div");
        addClass(this.headerEl, "yasgui-query-browser__header");
        this.bodyEl = document.createElement("div");
        addClass(this.bodyEl, "yasgui-query-browser__body");
        this.bodyEl.addEventListener("mousemove", (e) => {
            this.lastPointerPos = { x: e.clientX, y: e.clientY };
            if (this.tooltipEl.classList.contains("open"))
                this.positionTooltip();
        });
        this.footerEl = document.createElement("div");
        addClass(this.footerEl, "yasgui-query-browser__footer");
        this.statusEl = document.createElement("div");
        addClass(this.statusEl, "yasgui-query-browser__status");
        this.workspaceSelectEl = document.createElement("select");
        this.workspaceSelectEl.setAttribute("aria-label", "Select workspace");
        this.workspaceSelectEl.addEventListener("change", () => {
            this.selectedWorkspaceId = this.workspaceSelectEl.value || undefined;
            this.currentFolderId = undefined;
            this.resetTreeState();
            this.searchEl.value = "";
            void this.refresh();
        });
        this.searchEl = document.createElement("input");
        this.searchEl.type = "search";
        this.searchEl.placeholder = "Search queries";
        this.searchEl.setAttribute("aria-label", "Search managed queries by name");
        this.searchEl.addEventListener("input", () => {
            if (this.debouncedSearchHandle)
                window.clearTimeout(this.debouncedSearchHandle);
            this.debouncedSearchHandle = window.setTimeout(() => {
                void this.refresh();
            }, 250);
        });
        this.backButtonEl = document.createElement("button");
        this.backButtonEl.type = "button";
        this.backButtonEl.textContent = "Back";
        addClass(this.backButtonEl, "yasgui-query-browser__back");
        this.backButtonEl.setAttribute("aria-label", "Go to parent folder");
        this.backButtonEl.addEventListener("click", () => {
            if (!this.currentFolderId)
                return;
            const parts = this.currentFolderId.split("/").filter(Boolean);
            parts.pop();
            this.currentFolderId = parts.join("/") || undefined;
            void this.refresh();
        });
        const titleEl = document.createElement("div");
        addClass(titleEl, "yasgui-query-browser__title");
        titleEl.textContent = "Query Browser";
        const headerButtons = document.createElement("div");
        addClass(headerButtons, "yasgui-query-browser__header-buttons");
        const helpButton = document.createElement("button");
        helpButton.type = "button";
        addClass(helpButton, "yasgui-query-browser__help");
        helpButton.setAttribute("aria-label", "Open documentation");
        helpButton.innerHTML = '<i class="fas fa-circle-question"></i>';
        helpButton.addEventListener("click", () => {
            window.open("https://yasgui-doc.matdata.eu/docs/user-guide#managed-queries-and-workspaces", "_blank", "noopener,noreferrer");
        });
        const closeButton = document.createElement("button");
        closeButton.type = "button";
        addClass(closeButton, "yasgui-query-browser__close");
        closeButton.setAttribute("aria-label", "Close query browser");
        closeButton.innerHTML = '<i class="fas fa-xmark"></i>';
        closeButton.addEventListener("click", () => this.close());
        headerButtons.appendChild(helpButton);
        headerButtons.appendChild(closeButton);
        const headerControls = document.createElement("div");
        addClass(headerControls, "yasgui-query-browser__header-controls");
        headerControls.appendChild(this.workspaceSelectEl);
        headerControls.appendChild(this.searchEl);
        const headerTop = document.createElement("div");
        addClass(headerTop, "yasgui-query-browser__header-top");
        headerTop.appendChild(titleEl);
        headerTop.appendChild(headerButtons);
        this.headerEl.appendChild(headerTop);
        this.headerEl.appendChild(headerControls);
        this.listEl = document.createElement("div");
        addClass(this.listEl, "yasgui-query-browser__list");
        this.bodyEl.appendChild(this.backButtonEl);
        this.bodyEl.appendChild(this.statusEl);
        this.bodyEl.appendChild(this.listEl);
        this.footerEl.textContent = "";
        this.drawerEl.appendChild(this.headerEl);
        this.drawerEl.appendChild(this.bodyEl);
        this.drawerEl.appendChild(this.footerEl);
        this.rootEl.appendChild(this.drawerEl);
        this.rootEl.appendChild(this.tooltipEl);
        this.rootEl.addEventListener("click", (e) => {
            if (e.target === this.rootEl)
                this.close();
        });
        document.addEventListener("keydown", (e) => {
            if (!this.isOpen)
                return;
            if (e.key === "Escape") {
                e.preventDefault();
                this.close();
            }
        });
        this.close();
    }
    getElement() {
        return this.rootEl;
    }
    open(openerEl) {
        this.openerEl = openerEl;
        this.isOpen = true;
        this.rootEl.style.display = "flex";
        addClass(this.rootEl, "open");
        this.rootEl.setAttribute("aria-hidden", "false");
        this.drawerEl.setAttribute("role", "dialog");
        this.drawerEl.setAttribute("aria-modal", "true");
        void this.refresh().then(() => {
            this.workspaceSelectEl.focus();
        });
    }
    close() {
        this.isOpen = false;
        removeClass(this.rootEl, "open");
        this.rootEl.setAttribute("aria-hidden", "true");
        this.rootEl.style.display = "none";
        if (this.openerEl) {
            this.openerEl.focus();
            this.openerEl = undefined;
        }
    }
    toggle(openerEl) {
        if (this.isOpen)
            this.close();
        else
            this.open(openerEl);
    }
    getWorkspaces() {
        return this.yasgui.persistentConfig.getWorkspaces();
    }
    ensureWorkspaceSelection() {
        const workspaces = this.getWorkspaces();
        if (workspaces.length === 0) {
            this.selectedWorkspaceId = undefined;
            return;
        }
        if (!this.selectedWorkspaceId) {
            const persistedActive = this.yasgui.persistentConfig.getActiveWorkspaceId();
            this.selectedWorkspaceId = persistedActive || workspaces[0].id;
        }
        const exists = workspaces.some((w) => w.id === this.selectedWorkspaceId);
        if (!exists)
            this.selectedWorkspaceId = workspaces[0].id;
        this.yasgui.persistentConfig.setActiveWorkspaceId(this.selectedWorkspaceId);
    }
    renderWorkspaceSelect() {
        const workspaces = this.getWorkspaces();
        this.workspaceSelectEl.innerHTML = "";
        const placeholder = document.createElement("option");
        placeholder.value = "";
        placeholder.textContent = workspaces.length ? "Select workspace" : "No workspaces";
        this.workspaceSelectEl.appendChild(placeholder);
        for (const w of workspaces) {
            const opt = document.createElement("option");
            opt.value = w.id;
            opt.textContent = w.label;
            this.workspaceSelectEl.appendChild(opt);
        }
        this.workspaceSelectEl.value = this.selectedWorkspaceId || "";
    }
    setStatus(text) {
        this.statusEl.textContent = text;
    }
    resetTreeState() {
        this.treeWorkspaceId = undefined;
        this.expandedFolderIds.clear();
        this.folderEntriesById.clear();
        this.folderLoadingById.clear();
        this.folderErrorById.clear();
        this.invalidateRenderCache();
    }
    folderKey(folderId) {
        return folderId || "";
    }
    ensureFolderLoaded(backend, folderId) {
        return __awaiter(this, void 0, void 0, function* () {
            const key = this.folderKey(folderId);
            if (this.folderEntriesById.has(key) || this.folderLoadingById.has(key))
                return;
            this.folderLoadingById.add(key);
            this.folderErrorById.delete(key);
            try {
                const entries = yield backend.listFolder(folderId);
                this.folderEntriesById.set(key, entries);
            }
            catch (e) {
                const err = asWorkspaceBackendError(e);
                this.folderErrorById.set(key, err.message || "Failed to load folder");
                this.folderEntriesById.set(key, []);
            }
            finally {
                this.folderLoadingById.delete(key);
            }
        });
    }
    formatStatusError(err, workspaceType) {
        const message = err.message || "Unknown error";
        if (message.includes("No GitProviderClient configured")) {
            return "Git workspaces require a supported provider. Supported: GitHub, GitLab, Bitbucket Cloud (bitbucket.org), and Gitea. For self-hosted/enterprise instances, configure a git workspace 'provider' and/or 'apiBaseUrl'.";
        }
        if (message.toLowerCase().includes("not implemented")) {
            return "This workspace backend is not supported yet.";
        }
        return message;
    }
    clearList() {
        this.listEl.innerHTML = "";
    }
    renderEmptyWorkspaceState() {
        const emptyState = document.createElement("div");
        addClass(emptyState, "yasgui-query-browser__empty-state");
        const icon = document.createElement("div");
        addClass(icon, "yasgui-query-browser__empty-icon");
        icon.innerHTML = '<i class="fas fa-file-lines"></i>';
        const title = document.createElement("h3");
        addClass(title, "yasgui-query-browser__empty-title");
        title.textContent = "No Workspaces Configured";
        const description = document.createElement("p");
        addClass(description, "yasgui-query-browser__empty-description");
        description.innerHTML = `Workspaces allow you to save and manage SPARQL queries in a shared, versioned store. 
      You can use SPARQL endpoints (recommended) or Git repositories (GitHub, GitLab, Bitbucket, Gitea) to store your queries.`;
        const actionsContainer = document.createElement("div");
        addClass(actionsContainer, "yasgui-query-browser__empty-actions");
        const addWorkspaceBtn = document.createElement("button");
        addClass(addWorkspaceBtn, "yasgui-query-browser__empty-button");
        addClass(addWorkspaceBtn, "yasgui-query-browser__empty-button--primary");
        addWorkspaceBtn.textContent = "Add Workspace";
        addWorkspaceBtn.addEventListener("click", () => {
            this.close();
            const tab = this.yasgui.getTab();
            if (tab && tab.settingsModal) {
                const modal = tab.settingsModal;
                modal.open();
                setTimeout(() => {
                    var _a;
                    const workspacesButton = (_a = modal.modalContent) === null || _a === void 0 ? void 0 : _a.querySelector(".modalNavButton:nth-child(3)");
                    if (workspacesButton)
                        workspacesButton.click();
                }, 50);
            }
        });
        const learnMoreLink = document.createElement("a");
        addClass(learnMoreLink, "yasgui-query-browser__empty-link");
        learnMoreLink.href = "https://yasgui-doc.matdata.eu/docs/user-guide#managed-queries-and-workspaces";
        learnMoreLink.target = "_blank";
        learnMoreLink.rel = "noopener noreferrer";
        learnMoreLink.textContent = "Learn more about workspaces";
        actionsContainer.appendChild(addWorkspaceBtn);
        actionsContainer.appendChild(learnMoreLink);
        emptyState.appendChild(icon);
        emptyState.appendChild(title);
        emptyState.appendChild(description);
        emptyState.appendChild(actionsContainer);
        this.listEl.appendChild(emptyState);
    }
    formatQueryPreview(queryText, description) {
        const normalized = queryText.replace(/\r\n?/g, "\n").trim();
        const lines = normalized.split("\n").map((l) => l.trimEnd());
        const maxLines = 12;
        const selected = lines.slice(0, maxLines);
        let out = selected.join("\n");
        const maxChars = 800;
        if (out.length > maxChars)
            out = out.slice(0, maxChars - 1).trimEnd() + "…";
        if (lines.length > maxLines && !out.endsWith("…"))
            out = out + "\n…";
        const trimmedDesc = description === null || description === void 0 ? void 0 : description.replace(/\r\n?/g, "\n").trim();
        if (!trimmedDesc)
            return out;
        return `${trimmedDesc}\n\n${out}`;
    }
    ensureQueryPreview(backend, queryId) {
        return __awaiter(this, void 0, void 0, function* () {
            const cached = this.queryPreviewById.get(queryId);
            if (cached)
                return cached;
            if (this.queryPreviewLoadingById.has(queryId))
                return undefined;
            this.queryPreviewLoadingById.add(queryId);
            try {
                const res = yield backend.readQuery(queryId);
                const preview = this.formatQueryPreview(res.queryText, res.description);
                this.queryPreviewById.set(queryId, preview);
                return preview;
            }
            catch (_a) {
                return undefined;
            }
            finally {
                this.queryPreviewLoadingById.delete(queryId);
            }
        });
    }
    getManagedQueryIdFromMetadata(meta) {
        var _a, _b;
        if (meta.backendType === "git")
            return (_a = meta.queryRef) === null || _a === void 0 ? void 0 : _a.path;
        return (_b = meta.queryRef) === null || _b === void 0 ? void 0 : _b.managedQueryIri;
    }
    findOpenManagedTabId(workspaceId, backendType, queryId) {
        for (const tab of Object.values(this.yasgui._tabs)) {
            const meta = tab.getManagedQueryMetadata();
            if (!meta)
                continue;
            if (meta.workspaceId !== workspaceId)
                continue;
            if (meta.backendType !== backendType)
                continue;
            const openQueryId = this.getManagedQueryIdFromMetadata(meta);
            if (openQueryId === queryId)
                return tab.getId();
        }
        return undefined;
    }
    showTooltip(text) {
        this.tooltipEl.textContent = text;
        this.tooltipEl.setAttribute("aria-hidden", "false");
        addClass(this.tooltipEl, "open");
        this.positionTooltip();
    }
    hideTooltip() {
        this.tooltipEl.textContent = "";
        this.tooltipEl.setAttribute("aria-hidden", "true");
        removeClass(this.tooltipEl, "open");
    }
    positionTooltip() {
        const pos = this.lastPointerPos;
        if (!pos)
            return;
        let left = pos.x + 12;
        let top = pos.y + 12;
        const rect = this.tooltipEl.getBoundingClientRect();
        const vw = window.innerWidth;
        const vh = window.innerHeight;
        if (left + rect.width > vw - 8)
            left = Math.max(8, vw - rect.width - 8);
        if (top + rect.height > vh - 8)
            top = Math.max(8, vh - rect.height - 8);
        this.tooltipEl.style.left = `${left}px`;
        this.tooltipEl.style.top = `${top}px`;
    }
    attachQueryHoverPreview(row, backend, queryId) {
        const onEnter = () => {
            const cached = this.queryPreviewById.get(queryId);
            if (cached) {
                this.showTooltip(cached);
                return;
            }
            this.showTooltip("Loading preview…");
            void this.ensureQueryPreview(backend, queryId).then((preview) => {
                if (!this.tooltipEl.classList.contains("open"))
                    return;
                if (preview)
                    this.showTooltip(preview);
            });
        };
        const onLeave = () => {
            this.hideTooltip();
        };
        row.addEventListener("mouseenter", onEnter);
        row.addEventListener("mouseleave", onLeave);
        row.addEventListener("focusin", onEnter);
        row.addEventListener("focusout", onLeave);
    }
    getBackendForSelectedWorkspace() {
        const workspace = this.getWorkspaces().find((w) => w.id === this.selectedWorkspaceId);
        if (!workspace)
            return undefined;
        return getWorkspaceBackend(workspace, { persistentConfig: this.yasgui.persistentConfig });
    }
    versionRefFromVersionTag(backendType, versionTag) {
        if (!versionTag)
            return undefined;
        if (backendType === "git")
            return { commitSha: versionTag };
        return { managedQueryVersionIri: versionTag };
    }
    openManagedQueryInNewTab(backend, backendType, queryId, queryLabel) {
        return __awaiter(this, void 0, void 0, function* () {
            const workspaceId = this.selectedWorkspaceId;
            const alreadyOpenTabId = this.findOpenManagedTabId(workspaceId, backendType, queryId);
            if (alreadyOpenTabId) {
                this.yasgui.selectTabId(alreadyOpenTabId);
                return;
            }
            const read = yield backend.readQuery(queryId);
            const tab = this.yasgui.addTab(true);
            if (queryLabel)
                tab.setName(queryLabel);
            const endpoint = getEndpointToAutoSwitch(backendType, read);
            if (endpoint)
                tab.setEndpoint(endpoint);
            tab.setQuery(read.queryText);
            const managedMetadata = {
                workspaceId,
                backendType,
                queryRef: backendType === "git" ? { path: queryId } : { managedQueryIri: queryId },
                lastSavedVersionRef: this.versionRefFromVersionTag(backendType, read.versionTag),
                lastSavedTextHash: hashQueryText(read.queryText),
            };
            tab.setManagedQueryMetadata(managedMetadata);
        });
    }
    addQueryRowActions(row, backend, entry) {
        const actions = document.createElement("span");
        addClass(actions, "yasgui-query-browser__actions");
        if (entry.kind === "folder") {
            if (backend.renameFolder) {
                const renameBtn = document.createElement("button");
                renameBtn.type = "button";
                addClass(renameBtn, "yasgui-query-browser__action");
                renameBtn.textContent = "Rename";
                renameBtn.setAttribute("aria-label", `Rename folder ${entry.label}`);
                renameBtn.addEventListener("click", (e) => __awaiter(this, void 0, void 0, function* () {
                    e.preventDefault();
                    e.stopPropagation();
                    const next = window.prompt("Rename folder", entry.label);
                    if (!next)
                        return;
                    const trimmed = next.trim();
                    if (!trimmed || trimmed === entry.label)
                        return;
                    try {
                        yield backend.renameFolder(entry.id, trimmed);
                        this.folderEntriesById.clear();
                        this.invalidateRenderCache();
                        yield this.refresh();
                    }
                    catch (err) {
                        window.alert(asWorkspaceBackendError(err).message);
                    }
                }));
                actions.appendChild(renameBtn);
            }
            if (backend.deleteFolder) {
                const deleteBtn = document.createElement("button");
                deleteBtn.type = "button";
                addClass(deleteBtn, "yasgui-query-browser__action");
                addClass(deleteBtn, "yasgui-query-browser__action--danger");
                deleteBtn.textContent = "Delete";
                deleteBtn.setAttribute("aria-label", `Delete folder ${entry.label}`);
                deleteBtn.addEventListener("click", (e) => __awaiter(this, void 0, void 0, function* () {
                    e.preventDefault();
                    e.stopPropagation();
                    const ok = window.confirm(`Delete folder '${entry.label}' and everything inside it? This cannot be undone.`);
                    if (!ok)
                        return;
                    try {
                        yield backend.deleteFolder(entry.id);
                        this.folderEntriesById.clear();
                        this.invalidateRenderCache();
                        yield this.refresh();
                    }
                    catch (err) {
                        window.alert(asWorkspaceBackendError(err).message);
                    }
                }));
                actions.appendChild(deleteBtn);
            }
            if (actions.childElementCount > 0)
                row.appendChild(actions);
            return;
        }
        if (entry.kind !== "query")
            return;
        if (backend.renameQuery) {
            const renameBtn = document.createElement("button");
            renameBtn.type = "button";
            addClass(renameBtn, "yasgui-query-browser__action");
            renameBtn.textContent = "Rename";
            renameBtn.setAttribute("aria-label", `Rename ${entry.label}`);
            renameBtn.addEventListener("click", (e) => __awaiter(this, void 0, void 0, function* () {
                var _a, _b, _c, _d, _e, _f, _g;
                e.preventDefault();
                e.stopPropagation();
                const next = window.prompt("Rename query", entry.label);
                if (!next)
                    return;
                const trimmed = next.trim();
                if (!trimmed || trimmed === entry.label)
                    return;
                const gitRenameInfo = (() => {
                    if (backend.type !== "git")
                        return undefined;
                    const parts = entry.id.split("/").filter(Boolean);
                    parts.pop();
                    const folderPrefix = parts.join("/");
                    const safe = trimmed.replace(/[\\/]/g, "-");
                    const newFilename = normalizeQueryFilename(safe);
                    const newPath = folderPrefix ? `${folderPrefix}/${newFilename}` : newFilename;
                    return { oldPath: entry.id, newPath };
                })();
                const originalText = renameBtn.textContent;
                renameBtn.disabled = true;
                renameBtn.textContent = "Renaming…";
                addClass(renameBtn, "loading");
                try {
                    yield backend.renameQuery(entry.id, trimmed);
                    if (gitRenameInfo && gitRenameInfo.newPath && gitRenameInfo.oldPath) {
                        for (const tab of Object.values(this.yasgui._tabs)) {
                            const meta = (_b = (_a = tab).getManagedQueryMetadata) === null || _b === void 0 ? void 0 : _b.call(_a);
                            if (!meta)
                                continue;
                            if (meta.backendType !== "git")
                                continue;
                            if (meta.workspaceId !== this.selectedWorkspaceId)
                                continue;
                            const currentPath = (_c = meta.queryRef) === null || _c === void 0 ? void 0 : _c.path;
                            if (currentPath !== gitRenameInfo.oldPath)
                                continue;
                            try {
                                const read = yield backend.readQuery(gitRenameInfo.newPath);
                                const lastSavedTextHash = hashQueryText(read.queryText);
                                const lastSavedVersionRef = this.versionRefFromVersionTag("git", read.versionTag);
                                (_e = (_d = tab).setManagedQueryMetadata) === null || _e === void 0 ? void 0 : _e.call(_d, Object.assign(Object.assign({}, meta), { queryRef: Object.assign(Object.assign({}, meta.queryRef), { path: gitRenameInfo.newPath }), lastSavedTextHash,
                                    lastSavedVersionRef }));
                                (_g = (_f = tab).setName) === null || _g === void 0 ? void 0 : _g.call(_f, trimmed);
                            }
                            catch (_h) {
                            }
                        }
                    }
                    this.queryPreviewById.delete(entry.id);
                    this.folderEntriesById.clear();
                    this.invalidateRenderCache();
                    yield this.refresh();
                }
                catch (err) {
                    renameBtn.disabled = false;
                    renameBtn.textContent = originalText || "Rename";
                    removeClass(renameBtn, "loading");
                    window.alert(asWorkspaceBackendError(err).message);
                }
            }));
            actions.appendChild(renameBtn);
        }
        if (backend.deleteQuery) {
            const deleteBtn = document.createElement("button");
            deleteBtn.type = "button";
            addClass(deleteBtn, "yasgui-query-browser__action");
            addClass(deleteBtn, "yasgui-query-browser__action--danger");
            deleteBtn.textContent = "Delete";
            deleteBtn.setAttribute("aria-label", `Delete ${entry.label}`);
            deleteBtn.addEventListener("click", (e) => __awaiter(this, void 0, void 0, function* () {
                e.preventDefault();
                e.stopPropagation();
                const ok = window.confirm(`Delete '${entry.label}'? This cannot be undone.`);
                if (!ok)
                    return;
                const originalText = deleteBtn.textContent;
                deleteBtn.disabled = true;
                deleteBtn.textContent = "Deleting…";
                addClass(deleteBtn, "loading");
                try {
                    yield backend.deleteQuery(entry.id);
                    this.queryPreviewById.delete(entry.id);
                    this.folderEntriesById.clear();
                    this.invalidateRenderCache();
                    yield this.refresh();
                }
                catch (err) {
                    deleteBtn.disabled = false;
                    deleteBtn.textContent = originalText || "Delete";
                    removeClass(deleteBtn, "loading");
                    window.alert(asWorkspaceBackendError(err).message);
                }
            }));
            actions.appendChild(deleteBtn);
        }
        if (actions.childElementCount > 0) {
            row.appendChild(actions);
        }
    }
    renderFlatEntries(backend, entries) {
        this.clearList();
        const makeIndentStyle = (depth) => `padding-left: ${depth * 16}px;`;
        for (const entry of entries) {
            const row = document.createElement("div");
            addClass(row, "yasgui-query-browser__tree-row");
            addClass(row, entry.kind === "folder" ? "yasgui-query-browser__tree-row--folder" : "yasgui-query-browser__tree-row--query");
            row.setAttribute("role", "button");
            row.setAttribute("tabindex", "0");
            row.setAttribute("aria-label", entry.kind === "folder" ? `Open folder ${entry.label}` : `Open query ${entry.label}`);
            row.setAttribute("style", makeIndentStyle(0));
            if (entry.kind === "folder") {
                const isExpanded = this.expandedFolderIds.has(entry.id);
                const caret = document.createElement("span");
                addClass(caret, "yasgui-query-browser__tree-caret");
                caret.textContent = isExpanded ? "▾" : "▸";
                caret.setAttribute("aria-hidden", "true");
                const folderIcon = document.createElement("span");
                addClass(folderIcon, "yasgui-query-browser__tree-icon");
                folderIcon.textContent = "📁";
                const label = document.createElement("span");
                addClass(label, "yasgui-query-browser__tree-label");
                label.textContent = entry.label;
                row.appendChild(caret);
                row.appendChild(folderIcon);
                row.appendChild(label);
                this.addQueryRowActions(row, backend, entry);
            }
            else {
                const caretPlaceholder = document.createElement("span");
                addClass(caretPlaceholder, "yasgui-query-browser__tree-caret");
                caretPlaceholder.textContent = "▸";
                caretPlaceholder.setAttribute("aria-hidden", "true");
                addClass(caretPlaceholder, "yasgui-query-browser__tree-caret--placeholder");
                const icon = document.createElement("span");
                addClass(icon, "yasgui-query-browser__tree-icon");
                icon.textContent = "🕸️";
                const label = document.createElement("span");
                addClass(label, "yasgui-query-browser__tree-label");
                label.textContent = entry.label;
                row.appendChild(caretPlaceholder);
                row.appendChild(icon);
                row.appendChild(label);
                this.attachQueryHoverPreview(row, backend, entry.id);
                this.addQueryRowActions(row, backend, entry);
            }
            const activate = () => __awaiter(this, void 0, void 0, function* () {
                if (entry.kind === "folder") {
                    this.searchEl.value = "";
                    if (this.expandedFolderIds.has(entry.id)) {
                        this.expandedFolderIds.delete(entry.id);
                    }
                    else {
                        this.expandedFolderIds.add(entry.id);
                        yield this.ensureFolderLoaded(backend, entry.id);
                    }
                    yield this.refresh();
                    return;
                }
                yield this.openManagedQueryInNewTab(backend, backend.type, entry.id, entry.label);
                this.close();
            });
            row.addEventListener("keydown", (e) => {
                if (e.key === "Enter" || e.key === " ") {
                    e.preventDefault();
                    void activate();
                }
            });
            row.addEventListener("click", () => __awaiter(this, void 0, void 0, function* () {
                yield activate();
            }));
            this.listEl.appendChild(row);
        }
    }
    renderTree(backend) {
        this.clearList();
        const makeIndentStyle = (depth) => `padding-left: ${depth * 16}px;`;
        const renderFolderChildren = (folderId, depth) => {
            const key = this.folderKey(folderId);
            const entries = this.folderEntriesById.get(key) || [];
            const folders = entries
                .filter((e) => e.kind === "folder")
                .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" }));
            const queries = entries
                .filter((e) => e.kind === "query")
                .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" }));
            for (const entry of [...folders, ...queries]) {
                if (entry.kind === "folder") {
                    const isExpanded = this.expandedFolderIds.has(entry.id);
                    const row = document.createElement("div");
                    addClass(row, "yasgui-query-browser__tree-row");
                    addClass(row, "yasgui-query-browser__tree-row--folder");
                    row.setAttribute("aria-label", `${isExpanded ? "Collapse" : "Expand"} folder ${entry.label}`);
                    row.setAttribute("style", makeIndentStyle(depth));
                    row.setAttribute("role", "button");
                    row.setAttribute("tabindex", "0");
                    const caret = document.createElement("span");
                    addClass(caret, "yasgui-query-browser__tree-caret");
                    caret.textContent = isExpanded ? "▾" : "▸";
                    caret.setAttribute("aria-hidden", "true");
                    const folderIcon = document.createElement("span");
                    addClass(folderIcon, "yasgui-query-browser__tree-icon");
                    folderIcon.textContent = "📁";
                    const label = document.createElement("span");
                    addClass(label, "yasgui-query-browser__tree-label");
                    label.textContent = entry.label;
                    row.appendChild(caret);
                    row.appendChild(folderIcon);
                    row.appendChild(label);
                    this.addQueryRowActions(row, backend, entry);
                    const activate = () => __awaiter(this, void 0, void 0, function* () {
                        if (this.expandedFolderIds.has(entry.id)) {
                            this.expandedFolderIds.delete(entry.id);
                            yield this.refresh();
                            return;
                        }
                        this.expandedFolderIds.add(entry.id);
                        yield this.ensureFolderLoaded(backend, entry.id);
                        yield this.refresh();
                    });
                    row.addEventListener("keydown", (e) => {
                        if (e.key === "Enter" || e.key === " ") {
                            e.preventDefault();
                            void activate();
                        }
                    });
                    row.addEventListener("click", () => __awaiter(this, void 0, void 0, function* () {
                        yield activate();
                    }));
                    this.listEl.appendChild(row);
                    if (isExpanded) {
                        const childKey = this.folderKey(entry.id);
                        if (this.folderLoadingById.has(childKey)) {
                            const loading = document.createElement("div");
                            addClass(loading, "yasgui-query-browser__tree-meta");
                            loading.setAttribute("style", makeIndentStyle(depth + 1));
                            loading.textContent = "Loading…";
                            this.listEl.appendChild(loading);
                        }
                        const err = this.folderErrorById.get(childKey);
                        if (err) {
                            const error = document.createElement("div");
                            addClass(error, "yasgui-query-browser__tree-meta");
                            addClass(error, "yasgui-query-browser__tree-meta--error");
                            error.setAttribute("style", makeIndentStyle(depth + 1));
                            error.textContent = err;
                            this.listEl.appendChild(error);
                        }
                        renderFolderChildren(entry.id, depth + 1);
                    }
                }
                else {
                    const row = document.createElement("div");
                    addClass(row, "yasgui-query-browser__tree-row");
                    addClass(row, "yasgui-query-browser__tree-row--query");
                    row.setAttribute("aria-label", `Open query ${entry.label}`);
                    row.setAttribute("style", makeIndentStyle(depth));
                    row.setAttribute("role", "button");
                    row.setAttribute("tabindex", "0");
                    const caretPlaceholder = document.createElement("span");
                    addClass(caretPlaceholder, "yasgui-query-browser__tree-caret");
                    caretPlaceholder.textContent = "▸";
                    caretPlaceholder.setAttribute("aria-hidden", "true");
                    addClass(caretPlaceholder, "yasgui-query-browser__tree-caret--placeholder");
                    const icon = document.createElement("span");
                    addClass(icon, "yasgui-query-browser__tree-icon");
                    icon.textContent = "🕸️";
                    const label = document.createElement("span");
                    addClass(label, "yasgui-query-browser__tree-label");
                    label.textContent = entry.label;
                    row.appendChild(caretPlaceholder);
                    row.appendChild(icon);
                    row.appendChild(label);
                    this.attachQueryHoverPreview(row, backend, entry.id);
                    this.addQueryRowActions(row, backend, entry);
                    const activate = () => __awaiter(this, void 0, void 0, function* () {
                        yield this.openManagedQueryInNewTab(backend, backend.type, entry.id, entry.label);
                        this.close();
                    });
                    row.addEventListener("keydown", (e) => {
                        if (e.key === "Enter" || e.key === " ") {
                            e.preventDefault();
                            void activate();
                        }
                    });
                    row.addEventListener("click", () => __awaiter(this, void 0, void 0, function* () {
                        yield activate();
                    }));
                    this.listEl.appendChild(row);
                }
            }
        };
        renderFolderChildren(undefined, 0);
    }
    refresh() {
        return __awaiter(this, void 0, void 0, function* () {
            const runId = ++this.refreshRunId;
            this.ensureWorkspaceSelection();
            this.renderWorkspaceSelect();
            const workspaces = this.getWorkspaces();
            if (!this.selectedWorkspaceId || workspaces.length === 0) {
                this.backButtonEl.disabled = true;
                this.setStatus("");
                this.clearList();
                this.renderEmptyWorkspaceState();
                return;
            }
            const workspace = workspaces.find((w) => w.id === this.selectedWorkspaceId);
            if (!workspace)
                return;
            const backend = getWorkspaceBackend(workspace, { persistentConfig: this.yasgui.persistentConfig });
            const searchQuery = this.searchEl.value;
            this.backButtonEl.disabled = true;
            if (this.treeWorkspaceId !== workspace.id) {
                this.resetTreeState();
                this.treeWorkspaceId = workspace.id;
            }
            try {
                this.setStatus("Loading…");
                const trimmed = searchQuery.trim();
                if (trimmed) {
                    let entries;
                    if (backend.searchByName) {
                        entries = yield backend.searchByName(trimmed);
                    }
                    else {
                        const root = yield backend.listFolder(undefined);
                        entries = filterFolderEntriesByName(root, trimmed);
                    }
                    if (runId !== this.refreshRunId)
                        return;
                    const signature = entries.map((e) => this.entrySignature(e)).join("|");
                    this.setStatus(entries.length ? "" : "No results");
                    if (signature !== this.lastRenderedSignature) {
                        this.lastRenderedSignature = signature;
                        this.renderFlatEntries(backend, entries);
                    }
                    return;
                }
                yield this.ensureFolderLoaded(backend, undefined);
                if (runId !== this.refreshRunId)
                    return;
                const rootKey = this.folderKey(undefined);
                const rootEntries = this.folderEntriesById.get(rootKey) || [];
                const expandedIds = Array.from(this.expandedFolderIds).sort();
                const relevantFolderKeys = [rootKey, ...expandedIds.map((id) => this.folderKey(id))].sort();
                const foldersPart = relevantFolderKeys
                    .map((key) => {
                    const entries = this.folderEntriesById.get(key) || [];
                    const entryPart = entries
                        .map((e) => this.entrySignature(e))
                        .sort()
                        .join("|");
                    const loading = this.folderLoadingById.has(key) ? "1" : "0";
                    const err = this.folderErrorById.get(key) || "";
                    return `${key}:${loading}:${err}:${entryPart}`;
                })
                    .join(";");
                const signature = [
                    `rootCount:${rootEntries.length}`,
                    `expanded:${expandedIds.join(",")}`,
                    `folders:${foldersPart}`,
                ].join(";");
                this.setStatus(rootEntries.length ? "" : "No queries, add one by saving a tab to this workspace.");
                if (signature !== this.lastRenderedSignature) {
                    this.lastRenderedSignature = signature;
                    this.renderTree(backend);
                }
            }
            catch (e) {
                if (runId !== this.refreshRunId)
                    return;
                const err = asWorkspaceBackendError(e);
                this.setStatus(this.formatStatusError(err, workspace.type));
                this.clearList();
            }
        });
    }
}
//# sourceMappingURL=QueryBrowser.js.map