"use client";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback, useId, useRef, useState, } from "react";
import { cn } from "@stigmer/theme";
import { ResizableSplit } from "../internal/ResizableSplit.js";
import { editorKey, } from "../internal/store/index.js";
import { WorkspaceFileSearch } from "./WorkspaceFileSearch.js";
import { WorkspaceContentSearch } from "./WorkspaceContentSearch.js";
import { FileViewer } from "./FileViewer.js";
import { EditorTabs, editorTabDomId } from "./EditorTabs.js";
import { ExplorerTree } from "./ExplorerTree.js";
/** The built-in rail views every surface has. */
const BUILT_IN_VIEWS = ["files", "search"];
/**
 * A VS Code / Cursor-inspired read-only workspace surface: a slim activity rail
 * (Explorer / Search), a resizable sidebar, and a large editor area.
 *
 * This is the workspace-dominant counterpart to the inspector's cramped Viewer
 * tab. The session viewer flips its layout to give this surface the majority of
 * the screen while chat becomes a narrow column (see `SessionViewer`). It is a
 * self-contained organism: given `entries`, a `lister`, a `reader`, and the
 * open-file selection, it renders identically inside the Console or embedded in
 * a third-party host (DD-004). Explicitly read-only — no editing, no commit UI.
 *
 * All visual properties flow through `--stgm-*` tokens (DD-005).
 */
export function WorkspaceSurface({ entries, lister, reader, searcher, view, onViewChange, extraViews, virtualDocuments, onRemoveEntry, onAddLocalFolder, editors, selectedFile, onOpenFile, onActivateEditor, onPinEditor, onCloseEditor, onCollapse, change, reveal, className, }) {
    // Optionally controlled (standard React pattern): a host that owns view
    // routing (e.g. the session panel's auto-view logic) passes `view` +
    // `onViewChange`; standalone embedders get internal state for free.
    const [uncontrolledView, setUncontrolledView] = useState("files");
    const requestedView = view ?? uncontrolledView;
    const handleViewChange = useCallback((next) => {
        setUncontrolledView(next);
        onViewChange?.(next);
    }, [onViewChange]);
    // A stale id (e.g. a contextual extra view that disappeared) degrades to the
    // explorer rather than an empty sidebar.
    const isKnownView = BUILT_IN_VIEWS.includes(requestedView) ||
        (extraViews?.some((v) => v.id === requestedView) ?? false);
    const activeView = isKnownView ? requestedView : "files";
    const activeExtraView = extraViews?.find((v) => v.id === activeView);
    return (_jsxs("div", { className: cn("flex h-full min-h-0", className), children: [_jsx(ActivityRail, { view: activeView, onViewChange: handleViewChange, extraViews: extraViews }), _jsx(ResizableSplit, { resizablePane: "primary", defaultSize: 288, minSize: 200, maxSize: 560, storageKey: "stgm-workspace-sidebar-width", ariaLabel: "Resize sidebar", 
                // min-w-0: the split is a flex-row child of the surface; without it,
                // the fixed-width sidebar sub-pane gives the whole surface a hard
                // minimum width and content overflow escapes to the session panel.
                className: "min-h-0 min-w-0 flex-1", primary: activeExtraView ? (_jsx(ExtraViewSidebar, { view: activeExtraView })) : activeView === "search" ? (_jsx(SearchSidebar, { entries: entries, lister: lister, searcher: searcher, selectedFile: selectedFile, onOpenFile: onOpenFile })) : (_jsx(ExplorerSidebar, { entries: entries, lister: lister, selectedFile: selectedFile, onOpenFile: onOpenFile, onActivateFile: onPinEditor, onRemoveEntry: onRemoveEntry, onAddLocalFolder: onAddLocalFolder })), secondary: _jsx(EditorArea, { entries: entries, reader: reader, editors: editors, selectedFile: selectedFile, virtualDocuments: virtualDocuments, change: change, reveal: reveal, onActivateEditor: onActivateEditor, onPinEditor: onPinEditor, onCloseEditor: onCloseEditor, onCollapse: onCollapse }) })] }));
}
function ActivityRail({ view, onViewChange, extraViews, }) {
    const items = [
        { id: "files", label: "Explorer", icon: _jsx(FilesIcon, {}) },
        { id: "search", label: "Search", icon: _jsx(SearchIcon, {}) },
        ...(extraViews ?? []),
    ];
    // One ref per rail button so selection can move DOM focus with it (below).
    const buttonRefs = useRef([]);
    // Roving radiogroup: selection follows focus. `keydown` fires on the focused
    // button, so its `index` is the current position; we select the neighbour AND
    // move focus to it. Moving focus is essential — without it focus stays pinned
    // on the entered button (whose index never changes) and every later view
    // (Config/Changes/…) is unreachable by keyboard.
    const handleKeyDown = useCallback((e, index) => {
        let nextIndex = null;
        if (e.key === "ArrowDown" || e.key === "ArrowRight") {
            nextIndex = Math.min(index + 1, items.length - 1);
        }
        else if (e.key === "ArrowUp" || e.key === "ArrowLeft") {
            nextIndex = Math.max(index - 1, 0);
        }
        else if (e.key === "Home") {
            nextIndex = 0;
        }
        else if (e.key === "End") {
            nextIndex = items.length - 1;
        }
        if (nextIndex === null || nextIndex === index)
            return;
        e.preventDefault();
        onViewChange(items[nextIndex].id);
        buttonRefs.current[nextIndex]?.focus();
    }, [items, onViewChange]);
    return (_jsx("div", { role: "radiogroup", "aria-label": "Workspace view", "aria-orientation": "vertical", className: "flex w-11 shrink-0 flex-col items-center gap-1 border-r border-border bg-muted-faint py-2", children: items.map((item, index) => {
            const isSelected = view === item.id;
            const showBadge = item.badge != null && item.badge > 0;
            return (_jsxs("button", { ref: (el) => {
                    buttonRefs.current[index] = el;
                }, type: "button", role: "radio", "aria-checked": isSelected, "aria-label": showBadge ? `${item.label} (${item.badge})` : item.label, title: item.label, tabIndex: isSelected ? 0 : -1, onClick: () => onViewChange(item.id), onKeyDown: (e) => handleKeyDown(e, index), className: cn("relative flex h-9 w-9 items-center justify-center rounded-md transition-colors", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", isSelected
                    ? "bg-accent text-foreground"
                    : "text-muted-foreground hover:bg-accent-hover hover:text-foreground"), children: [item.icon, showBadge && (_jsx("span", { "aria-hidden": "true", className: "absolute -right-0.5 -top-0.5 inline-flex min-w-[0.875rem] items-center justify-center rounded-full bg-primary px-1 py-px text-[9px] font-medium leading-none text-primary-foreground", children: item.badge }))] }, item.id));
        }) }));
}
// ---------------------------------------------------------------------------
// Explorer sidebar — multi-root file tree
// ---------------------------------------------------------------------------
function ExplorerSidebar({ entries, lister, selectedFile, onOpenFile, onActivateFile, onRemoveEntry, onAddLocalFolder, }) {
    return (_jsxs("div", { className: "flex h-full min-h-0 flex-col border-r border-border", children: [_jsx(SidebarHeader, { title: "Explorer" }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto py-1.5", children: !lister || entries.length === 0 ? (_jsx(SidebarEmpty, { children: "No workspace attached." })) : (_jsx(ExplorerTree, { entries: entries, lister: lister, selectedFile: selectedFile, onOpenFile: onOpenFile, onActivateFile: onActivateFile, onRemoveEntry: onRemoveEntry })) }), onAddLocalFolder && (_jsx("div", { className: "shrink-0 border-t border-border-muted p-1.5", children: _jsxs("button", { type: "button", onClick: onAddLocalFolder, className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs text-foreground transition-colors hover:bg-accent-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [_jsx(FolderPlusIcon, {}), _jsx("span", { className: "flex-1 text-left", children: "Add Folder" })] }) }))] }));
}
function SearchSidebar({ entries, lister, searcher, selectedFile, onOpenFile, }) {
    // Filename search is the default: cached, instant, and always available.
    // Content (text) search is opt-in and only offered when a searcher exists.
    const [mode, setMode] = useState("name");
    return (_jsxs("div", { className: "flex h-full min-h-0 flex-col border-r border-border", children: [_jsx(SidebarHeader, { title: "Search" }), searcher && _jsx(SearchModeToggle, { value: mode, onChange: setMode }), searcher && mode === "text" ? (_jsx(WorkspaceContentSearch, { entries: entries, searcher: searcher, onOpenFile: onOpenFile, selectedFile: selectedFile, className: "min-h-0 flex-1" })) : (_jsx(WorkspaceFileSearch, { entries: entries, lister: lister, onOpenFile: onOpenFile, selectedFile: selectedFile, className: "min-h-0 flex-1" }))] }));
}
/**
 * `Name | Text` search-mode selector, mirroring `FileViewer`'s `Diff | File`
 * radiogroup (roving `tabindex`, arrow-key roving, `role="radio"`). Shown only
 * when a content searcher is injected.
 */
function SearchModeToggle({ value, onChange, }) {
    const options = [
        { value: "name", label: "Name" },
        { value: "text", label: "Text" },
    ];
    // Selection follows focus, matching ActivityRail / ViewerModeToggle.
    const buttonRefs = useRef([]);
    const selectMode = useCallback((next) => {
        onChange(next);
        buttonRefs.current[next === "name" ? 0 : 1]?.focus();
    }, [onChange]);
    const handleKeyDown = useCallback((e) => {
        if (e.key === "ArrowRight" || e.key === "ArrowDown") {
            e.preventDefault();
            selectMode("text");
        }
        else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
            e.preventDefault();
            selectMode("name");
        }
    }, [selectMode]);
    return (_jsx("div", { className: "shrink-0 border-b border-border px-2 py-1.5", children: _jsx("div", { role: "radiogroup", "aria-label": "Search mode", className: "inline-flex rounded-md bg-muted p-0.5", children: options.map((option, index) => {
                const isSelected = value === option.value;
                return (_jsx("button", { ref: (el) => {
                        buttonRefs.current[index] = el;
                    }, type: "button", role: "radio", "aria-checked": isSelected, tabIndex: isSelected ? 0 : -1, onClick: () => onChange(option.value), onKeyDown: handleKeyDown, className: cn("rounded px-2.5 py-0.5 text-[0.7rem] font-medium transition-colors", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", isSelected
                        ? "bg-background text-foreground shadow-sm"
                        : "text-muted-foreground hover:text-foreground"), children: option.label }, option.value));
            }) }) }));
}
// ---------------------------------------------------------------------------
// Extra-view sidebar — host-injected facet content (Config, Changes, …)
// ---------------------------------------------------------------------------
function ExtraViewSidebar({ view }) {
    return (_jsxs("div", { className: "flex h-full min-h-0 flex-col border-r border-border", children: [_jsx(SidebarHeader, { title: view.label }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 py-3", children: view.content })] }));
}
// ---------------------------------------------------------------------------
// Editor area — collapse control + tab strip + read-only FileViewer
// ---------------------------------------------------------------------------
function EditorArea({ entries, reader, editors, selectedFile, virtualDocuments, change, reveal, onActivateEditor, onPinEditor, onCloseEditor, onCollapse, }) {
    const viewerRef = useRef(null);
    // Instance-scoped ids tie each tab to the single editor body (tabpanel).
    const idBase = useId();
    const panelId = `${idBase}editor-panel`;
    const tabIdPrefix = `${idBase}editor-tab-`;
    const activeKey = selectedFile
        ? editorKey(selectedFile.entryId, selectedFile.path)
        : null;
    // The active tab is a virtual document when its identity matches an injected
    // one — the body renders the document's content instead of the file viewer.
    const activeVirtualDocument = activeKey !== null
        ? virtualDocuments?.find((d) => editorKey(d.entryId, d.path) === activeKey)
        : undefined;
    const activeEntryName = selectedFile
        ? entries.find((e) => e.id === selectedFile.entryId)?.name
        : undefined;
    return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { className: "flex shrink-0 items-stretch border-b border-border pr-24", children: [_jsx("button", { type: "button", onClick: onCollapse, "aria-label": "Back to chat", title: "Back to chat", className: "flex shrink-0 items-center border-r border-border px-2 text-muted-foreground transition-colors hover:bg-accent-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring", children: _jsx(ChevronLeftIcon, {}) }), editors.length > 0 && (_jsx(EditorTabs, { editors: editors, activeKey: activeKey, onActivate: onActivateEditor, onPin: onPinEditor, onClose: onCloseEditor, panelId: panelId, tabIdPrefix: tabIdPrefix, className: "min-w-0 flex-1 border-b-0" }))] }), _jsx("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", ...(activeKey
                    ? {
                        role: "tabpanel",
                        id: panelId,
                        "aria-labelledby": editorTabDomId(tabIdPrefix, activeKey),
                    }
                    : {}), children: activeVirtualDocument ? (
                // A virtual document owns its body wholesale: no breadcrumbs (there is
                // no filesystem location) and no FileViewer (there is no reader-backed
                // file). Keyed like files so switching documents resets cleanly.
                // Vertical-only scrolling: a lone overflow-y-auto would compute
                // overflow-x to auto too, giving the pane a horizontal scrollbar the
                // moment any child refuses to shrink — documents must reflow instead.
                _jsx("div", { className: "min-h-0 min-w-0 flex-1 overflow-y-auto overflow-x-hidden", children: activeVirtualDocument.content }, activeKey ?? undefined)) : selectedFile ? (_jsxs(_Fragment, { children: [_jsx(Breadcrumbs, { entryName: activeEntryName, path: selectedFile.path, onRefresh: reader ? () => viewerRef.current?.refresh() : undefined }), _jsx(FileViewer, { ref: viewerRef, selectedFile: selectedFile, entries: entries, reader: reader, change: change, reveal: reveal, onClose: onCollapse, showHeader: false, className: "min-h-0 flex-1" }, activeKey ?? undefined)] })) : (_jsx("div", { className: "flex flex-1 items-center justify-center p-8 text-center text-xs text-muted-foreground", children: "Select a file to view its contents." })) })] }));
}
/**
 * Location breadcrumb for the active file: entry › folder › … › file, with an
 * optional refresh. Display-only orientation (clickable navigation is a future
 * enhancement); the refresh re-fetches the live file content.
 */
function Breadcrumbs({ entryName, path, onRefresh, }) {
    const segments = path.split("/").filter(Boolean);
    const crumbs = entryName ? [entryName, ...segments] : segments;
    return (_jsxs("div", { className: "flex shrink-0 items-center gap-1 border-b border-border-muted px-3 py-1 text-[0.65rem] text-muted-foreground", children: [_jsx("nav", { "aria-label": "File location", className: "flex min-w-0 flex-1 items-center gap-1", children: crumbs.map((crumb, i) => {
                    const isLast = i === crumbs.length - 1;
                    return (_jsxs("span", { className: "flex min-w-0 items-center gap-1", children: [i > 0 && _jsx("span", { className: "text-muted-foreground-subtle", "aria-hidden": "true", children: "\u203A" }), _jsx("span", { className: cn("truncate", isLast && "text-foreground"), children: crumb })] }, i));
                }) }), onRefresh && (_jsx("button", { type: "button", onClick: onRefresh, "aria-label": "Reload file", title: "Reload file", className: "shrink-0 rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", children: _jsx(RefreshIcon, {}) }))] }));
}
// ---------------------------------------------------------------------------
// Shared sidebar chrome
// ---------------------------------------------------------------------------
function SidebarHeader({ title }) {
    return (_jsx("div", { className: "shrink-0 border-b border-border px-3 py-2 text-[0.65rem] font-semibold uppercase tracking-wide text-muted-foreground", children: title }));
}
function SidebarEmpty({ children }) {
    return (_jsx("div", { className: "px-2 py-6 text-center text-xs text-muted-foreground", children: children }));
}
// ---------------------------------------------------------------------------
// Inline SVG icons (SDK independence — no lucide dependency)
// ---------------------------------------------------------------------------
function FilesIcon() {
    return (_jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M13 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), _jsx("path", { d: "M13 3v5h5" })] }));
}
function SearchIcon() {
    return (_jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("circle", { cx: "11", cy: "11", r: "7" }), _jsx("path", { d: "M21 21l-4.3-4.3" })] }));
}
function FolderPlusIcon() {
    return (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: "shrink-0 text-muted-foreground", "aria-hidden": "true", children: [_jsx("path", { d: "M1.5 3.5V11a1 1 0 001 1h9a1 1 0 001-1V5.5a1 1 0 00-1-1H7L5.5 3H2.5a1 1 0 00-1 .5z" }), _jsx("path", { d: "M7 7v3M5.5 8.5h3" })] }));
}
function ChevronLeftIcon() {
    return (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M10 3L5 8L10 13" }) }));
}
function RefreshIcon() {
    return (_jsxs("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M1.5 6a4.5 4.5 0 017.794-3.073M10.5 6a4.5 4.5 0 01-7.794 3.073" }), _jsx("path", { d: "M9.5 1v2.5H7M2.5 11V8.5H5" })] }));
}
//# sourceMappingURL=WorkspaceSurface.js.map