"use client";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useState, useCallback, useEffect, useRef, useMemo, useId, } from "react";
import { cn } from "@stigmer/theme";
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
import { VendorApprovalStatus } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/spec_pb";
import { useMcpServerSearch } from "./useMcpServerSearch";
import { useScrollShadows } from "../internal/useScrollShadows";
import { ScrollFade } from "../internal/ScrollFade";
import { McpServerConfigPanel } from "./McpServerConfigPanel";
import { useMcpServerOAuthConnect } from "./useMcpServerOAuthConnect";
// ---------------------------------------------------------------------------
// Utilities
// ---------------------------------------------------------------------------
function refKey(ref) {
    return `${ref.org}/${ref.slug}`;
}
function usageKey(usage) {
    return refKey(usage.mcpServerRef);
}
function refFromServerKey(key) {
    const idx = key.indexOf("/");
    return {
        org: key.slice(0, idx),
        slug: key.slice(idx + 1),
        kind: ApiResourceKind.mcp_server,
    };
}
function slugFromServerKey(key) {
    const idx = key.indexOf("/");
    return key.slice(idx + 1);
}
// ---------------------------------------------------------------------------
// Main component
// ---------------------------------------------------------------------------
/**
 * Searchable picker for selecting MCP servers from the platform.
 *
 * Renders a search input and scrollable list of available MCP servers.
 * Designed to be placed inside a popover container — this component
 * renders the picker content, not the popover shell.
 *
 * Operates in two modes:
 *
 * **Simple mode** (default) — toggle servers on/off. Selected servers
 * produce `McpServerUsageInput[]` via `value`/`onChange`. No setup
 * flow, no per-tool selection.
 *
 * **Setup mode** (when `setup` is provided) — each selected server
 * shows its setup status (loading, needs credentials, ready). Users
 * can drill into a per-server configuration panel to provide
 * credentials and customize which tools to enable. Selection is
 * managed through `setup.entries` and the setup callbacks.
 *
 * All visual properties flow through `--stgm-*` tokens.
 *
 * @example
 * ```tsx
 * // Simple mode:
 * <McpServerPicker
 *   org="acme"
 *   value={mcpUsages}
 *   onChange={setMcpUsages}
 * />
 *
 * // Setup mode:
 * const mcpSetup = useMcpServerSetup("acme");
 * <McpServerPicker
 *   org="acme"
 *   setup={{
 *     entries: mcpSetup.entries,
 *     onServerAdded: (ref) => mcpSetup.addServer(ref),
 *     onServerRemoved: (ref) => mcpSetup.removeServer(ref),
 *     onSubmitEnvVars: (ref, v, o) => mcpSetup.submitEnvVars(ref, v, o),
 *     onEnabledToolsChange: (ref, t) => mcpSetup.setEnabledTools(ref, t),
 *   }}
 * />
 * ```
 */
export function McpServerPicker({ org, scope, value, onChange, onDisplayNameResolved, disabled, className, setup, initialServerKey, poolValues, activeOrg, }) {
    const instanceId = useId();
    const listId = `${instanceId}-list`;
    const { results, isLoading, error, query, setQuery } = useMcpServerSearch(org, { scope });
    const oauth = useMcpServerOAuthConnect();
    const [focusIndex, setFocusIndex] = useState(-1);
    const [view, setView] = useState(() => initialServerKey
        ? { type: "configure", serverKey: initialServerKey }
        : { type: "list" });
    const [manualOverrideKeys, setManualOverrideKeys] = useState(() => new Set());
    const searchRef = useRef(null);
    const results_ = useScrollShadows();
    const selected_ = useScrollShadows();
    // -----------------------------------------------------------------------
    // Derive selected keys from setup entries or value
    // -----------------------------------------------------------------------
    const selectedKeys = useMemo(() => {
        if (setup)
            return new Set(Object.keys(setup.entries));
        return new Set((value ?? []).map(usageKey));
    }, [setup, value]);
    const selectedCount = selectedKeys.size;
    const availableResults = useMemo(() => results.filter((r) => !selectedKeys.has(`${r.org}/${r.slug}`)), [results, selectedKeys]);
    // -----------------------------------------------------------------------
    // Reset drill-in view when the configured entry disappears
    // -----------------------------------------------------------------------
    useEffect(() => {
        if (view.type === "configure" &&
            (!setup || !(view.serverKey in setup.entries))) {
            setView({ type: "list" });
        }
    }, [view, setup]);
    useEffect(() => {
        setFocusIndex(-1);
    }, [query]);
    useEffect(() => {
        if (focusIndex >= 0) {
            results_.scrollRef.current
                ?.querySelector(`[data-idx="${focusIndex}"]`)
                ?.scrollIntoView({ block: "nearest" });
        }
    }, [focusIndex, results_.scrollRef]);
    // -----------------------------------------------------------------------
    // Selection handlers — branch on setup presence
    // -----------------------------------------------------------------------
    const handleSelect = useCallback((result) => {
        const ref = {
            org: result.org,
            slug: result.slug,
            kind: ApiResourceKind.mcp_server,
        };
        if (setup) {
            setup.onServerAdded(ref);
        }
        else {
            onChange?.([...(value ?? []), { mcpServerRef: ref }]);
        }
        onDisplayNameResolved?.(`${result.org}/${result.slug}`, result.name);
    }, [setup, value, onChange, onDisplayNameResolved]);
    const handleRemove = useCallback((key) => {
        if (view.type === "configure" && view.serverKey === key) {
            setView({ type: "list" });
        }
        if (setup) {
            setup.onServerRemoved(refFromServerKey(key));
        }
        else {
            onChange?.((value ?? []).filter((u) => usageKey(u) !== key));
        }
    }, [setup, value, onChange, view]);
    const handleSearchKeyDown = useCallback((e) => {
        if (e.key === "ArrowDown") {
            e.preventDefault();
            setFocusIndex((prev) => prev < availableResults.length - 1 ? prev + 1 : prev);
        }
        else if (e.key === "ArrowUp") {
            e.preventDefault();
            setFocusIndex((prev) => (prev > 0 ? prev - 1 : -1));
        }
        else if (e.key === "Enter" &&
            focusIndex >= 0 &&
            focusIndex < availableResults.length) {
            e.preventDefault();
            handleSelect(availableResults[focusIndex]);
        }
        else if (e.key === "Enter") {
            e.preventDefault();
        }
    }, [availableResults, focusIndex, handleSelect]);
    // -----------------------------------------------------------------------
    // Configure view — map entry to McpServerConfigPanel props
    // -----------------------------------------------------------------------
    if (view.type === "configure" && setup) {
        const entry = setup.entries[view.serverKey];
        if (!entry || entry.status === "loading") {
            setView({ type: "list" });
            return null;
        }
        const ref = refFromServerKey(view.serverKey);
        const needsCredentials = entry.status === "needsSetup" || entry.status === "submitting";
        const auth = entry.mcpServer.spec?.auth;
        const oauthTargetEnvVar = auth?.targetEnvVar || null;
        const hasOAuth = !!auth;
        const isManualOverride = manualOverrideKeys.has(view.serverKey);
        const entryMissingVars = entry.status === "needsSetup" ? entry.missingVariables : [];
        const filteredMissingVars = oauthTargetEnvVar && !isManualOverride
            ? entryMissingVars.filter((v) => v.key !== oauthTargetEnvVar)
            : entryMissingVars;
        const hasManualVars = filteredMissingVars.length > 0;
        const oauthTokenMissing = oauthTargetEnvVar
            ? entryMissingVars.some((v) => v.key === oauthTargetEnvVar)
            : false;
        const oauthStatus = entry.mcpServer.status?.oauthStatus;
        const isVendorApprovalPending = hasOAuth &&
            oauthStatus?.vendorApprovalStatus === VendorApprovalStatus.PENDING;
        const isVendorApprovalBlocked = hasOAuth &&
            (oauthStatus?.vendorApprovalStatus === VendorApprovalStatus.PENDING ||
                oauthStatus?.vendorApprovalStatus === VendorApprovalStatus.REJECTED);
        const oauthSignInProps = hasOAuth && !isManualOverride
            ? {
                onSignIn: async () => {
                    if (!entry.mcpServer.metadata?.id)
                        return;
                    try {
                        await oauth.startOAuth(entry.mcpServer.metadata.id, activeOrg ?? org);
                        setup.onServerAdded(ref);
                    }
                    catch {
                        // error state managed by oauth hook
                    }
                },
                phase: oauth.phase,
                isConnected: !oauthTokenMissing,
                error: oauth.error,
                onClearError: oauth.clearError,
                isVendorApprovalPending,
                isVendorApprovalBlocked,
                vendorApprovalDocsUrl: oauthStatus?.vendorApprovalDocsUrl || null,
            }
            : undefined;
        const handleSwitchToManual = hasOAuth
            ? () => {
                setManualOverrideKeys((prev) => {
                    const next = new Set(prev);
                    next.add(view.serverKey);
                    return next;
                });
            }
            : undefined;
        const handleSwitchToOAuth = hasOAuth && isManualOverride
            ? () => {
                setManualOverrideKeys((prev) => {
                    const next = new Set(prev);
                    next.delete(view.serverKey);
                    return next;
                });
            }
            : undefined;
        return (_jsx("div", { className: cn("w-72", className), children: _jsx(McpServerConfigPanel, { mcpServer: entry.mcpServer, oauthSignIn: oauthSignInProps, credentials: needsCredentials && hasManualVars
                    ? {
                        variables: filteredMissingVars,
                        onSubmit: (values, opts) => setup.onSubmitEnvVars(ref, values, opts),
                        isSubmitting: entry.status === "submitting",
                        poolValues,
                    }
                    : undefined, onSwitchToManual: handleSwitchToManual, onSwitchToOAuth: handleSwitchToOAuth, discoveredTools: entry.discoveredTools, toolApprovals: entry.toolApprovals, enabledTools: entry.status === "ready"
                    ? entry.enabledTools
                    : entry.discoveredTools.map((t) => t.name), onEnabledToolsChange: (tools) => setup.onEnabledToolsChange(ref, tools), onBack: () => setView({ type: "list" }), error: entry.error, disabled: disabled }) }));
    }
    // -----------------------------------------------------------------------
    // List view
    // -----------------------------------------------------------------------
    return (_jsxs("div", { className: cn("space-y-2 w-72", className), children: [selectedCount > 0 && (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "text-[0.65rem] font-medium text-muted-foreground", children: "Selected" }), _jsxs("div", { className: "relative", children: [selected_.canScrollUp && _jsx(ScrollFade, { position: "top" }), _jsx("div", { ref: selected_.scrollRef, className: "max-h-28 space-y-1 overflow-y-auto", children: setup
                                    ? Object.entries(setup.entries).map(([key, entry]) => (_jsx(SetupServerRow, { serverKey: key, entry: entry, onConfigure: () => setView({ type: "configure", serverKey: key }), onRetry: () => setup.onServerAdded(refFromServerKey(key)), onRemove: () => handleRemove(key), disabled: disabled }, key)))
                                    : (value ?? []).map((usage) => {
                                        const key = usageKey(usage);
                                        return (_jsx(SimpleServerRow, { slug: usage.mcpServerRef.slug, onRemove: () => handleRemove(key), disabled: disabled }, key));
                                    }) }), selected_.canScrollDown && _jsx(ScrollFade, { position: "bottom" })] })] })), _jsx("input", { ref: searchRef, type: "text", role: "combobox", "aria-expanded": true, "aria-controls": listId, "aria-activedescendant": focusIndex >= 0 ? `${instanceId}-opt-${focusIndex}` : undefined, placeholder: "Search MCP servers...", value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: handleSearchKeyDown, disabled: disabled, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", autoFocus: true }), error && _jsx("p", { className: "text-xs text-destructive", children: error.message }), _jsxs("div", { className: "relative", children: [results_.canScrollUp && _jsx(ScrollFade, { position: "top" }), _jsx("div", { ref: results_.scrollRef, id: listId, role: "listbox", "aria-label": "MCP Servers", className: "max-h-52 overflow-y-auto", children: isLoading ? (_jsx(LoadingSkeleton, {})) : availableResults.length === 0 ? (_jsx("div", { className: "py-4 text-center text-xs text-muted-foreground", children: query
                                ? "No MCP servers match your search"
                                : selectedCount > 0
                                    ? "All available servers selected"
                                    : "No MCP servers found" })) : (availableResults.map((result, idx) => (_jsxs("button", { id: `${instanceId}-opt-${idx}`, type: "button", "data-idx": idx, onClick: () => handleSelect(result), disabled: disabled, className: cn("group flex w-full flex-col gap-0.5 rounded-md px-2 py-1.5 text-left text-xs transition-colors", "disabled:pointer-events-none disabled:opacity-50", idx === focusIndex
                                ? "bg-accent text-foreground"
                                : "text-foreground hover:bg-accent-hover"), role: "option", "aria-selected": idx === focusIndex, children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(McpServerIcon, {}), _jsx("span", { className: "truncate font-medium", children: _jsx(HighlightMatch, { text: result.name, query: query }) }), _jsx("span", { className: "ml-auto shrink-0 text-[0.6rem] text-muted-foreground", children: result.org })] }), result.description && (_jsx("span", { className: cn("pl-5 text-[0.65rem] text-muted-foreground", idx !== focusIndex &&
                                        "line-clamp-2 group-hover:line-clamp-none"), children: result.description }))] }, result.id)))) }), results_.canScrollDown && _jsx(ScrollFade, { position: "bottom" })] })] }));
}
// ---------------------------------------------------------------------------
// Simple mode row (backward-compatible rendering)
// ---------------------------------------------------------------------------
function SimpleServerRow({ slug, onRemove, disabled, }) {
    return (_jsxs("div", { className: "flex items-center gap-2 rounded-md bg-muted-faint px-2 py-1 text-xs", children: [_jsx(McpServerIcon, {}), _jsx("span", { className: "min-w-0 flex-1 truncate text-foreground", children: slug }), _jsx("button", { type: "button", onClick: onRemove, disabled: disabled, className: "shrink-0 text-muted-foreground hover:text-destructive disabled:pointer-events-none", "aria-label": `Remove ${slug}`, children: _jsx(XIcon, {}) })] }));
}
// ---------------------------------------------------------------------------
// Setup mode row — per-server status indicators
// ---------------------------------------------------------------------------
function SetupServerRow({ serverKey, entry, onConfigure, onRetry, onRemove, disabled, }) {
    const slug = slugFromServerKey(serverKey);
    const isSubmitting = entry.status === "submitting";
    const hasError = entry.error != null;
    return (_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: cn("flex items-center gap-2 rounded-md px-2 py-1 text-xs", hasError ? "bg-destructive-subtle" : "bg-muted-faint"), children: [_jsx(StatusIndicator, { status: entry.status, hasError: hasError }), _jsx("span", { className: "min-w-0 flex-1 truncate text-foreground", children: slug }), entry.status === "loading" && !hasError && (_jsx("span", { className: "shrink-0 text-[0.6rem] text-muted-foreground", children: "Loading\u2026" })), entry.status === "loading" && hasError && (_jsx("button", { type: "button", onClick: onRetry, disabled: disabled, className: cn("shrink-0 rounded px-1.5 py-0.5 text-[0.6rem] font-medium", "text-destructive hover:bg-destructive-subtle", "disabled:pointer-events-none disabled:opacity-50"), children: "Retry" })), entry.status === "needsSetup" && (_jsx("button", { type: "button", onClick: onConfigure, disabled: disabled, className: cn("shrink-0 rounded px-1.5 py-0.5 text-[0.6rem] font-medium", "text-warning hover:bg-warning/10", "disabled:pointer-events-none disabled:opacity-50"), children: "Configure" })), isSubmitting && (_jsx("span", { className: "shrink-0 text-[0.6rem] text-muted-foreground", children: "Saving\u2026" })), entry.status === "ready" && (_jsxs("button", { type: "button", onClick: onConfigure, disabled: disabled, className: cn("shrink-0 flex items-center gap-0.5 rounded px-1.5 py-0.5 text-[0.6rem] font-medium", "text-muted-foreground hover:text-foreground hover:bg-accent-hover", "disabled:pointer-events-none disabled:opacity-50"), children: [entry.discoveredTools.length > 0 && (_jsxs("span", { children: [entry.enabledTools.length, "/", entry.discoveredTools.length, " tools"] })), _jsx(ChevronRightIcon, {})] })), _jsx("button", { type: "button", onClick: onRemove, disabled: disabled || isSubmitting, className: "shrink-0 text-muted-foreground hover:text-destructive disabled:pointer-events-none disabled:opacity-50", "aria-label": `Remove ${slug}`, children: _jsx(XIcon, {}) })] }), hasError && entry.status === "loading" && (_jsx("p", { className: "px-2 text-[0.6rem] leading-relaxed text-destructive line-clamp-2", children: entry.error.message }))] }));
}
// ---------------------------------------------------------------------------
// Status indicator dot/spinner
// ---------------------------------------------------------------------------
function StatusIndicator({ status, hasError, }) {
    if (hasError) {
        return (_jsx("span", { className: "inline-block size-2 shrink-0 rounded-full bg-destructive", "aria-label": "Error" }));
    }
    switch (status) {
        case "loading":
        case "submitting":
            return _jsx(SmallSpinner, {});
        case "needsSetup":
            return (_jsx("span", { className: "inline-block size-2 shrink-0 rounded-full bg-warning", "aria-label": "Needs setup" }));
        case "ready":
            return (_jsx("span", { className: "inline-block size-2 shrink-0 rounded-full bg-emerald-500", "aria-label": "Ready" }));
    }
}
// ---------------------------------------------------------------------------
// Internal components
// ---------------------------------------------------------------------------
function HighlightMatch({ text, query }) {
    if (!query)
        return _jsx(_Fragment, { children: text });
    const idx = text.toLowerCase().indexOf(query.toLowerCase());
    if (idx === -1)
        return _jsx(_Fragment, { children: text });
    return (_jsxs(_Fragment, { children: [text.slice(0, idx), _jsx("span", { className: "font-semibold", children: text.slice(idx, idx + query.length) }), text.slice(idx + query.length)] }));
}
function LoadingSkeleton() {
    return (_jsx("div", { className: "space-y-1 py-1", children: [60, 75, 50, 68].map((w, i) => (_jsxs("div", { className: "flex flex-col gap-1 px-2 py-1.5", children: [_jsx("div", { className: "h-3 rounded bg-muted animate-pulse", style: { width: `${w}%` } }), _jsx("div", { className: "h-2 rounded bg-muted-subtle animate-pulse", style: { width: `${Math.min(w + 15, 90)}%` } })] }, i))) }));
}
// ---------------------------------------------------------------------------
// Icons
// ---------------------------------------------------------------------------
function McpServerIcon() {
    return (_jsxs("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: "shrink-0 text-muted-foreground", "aria-hidden": "true", children: [_jsx("rect", { x: "2", y: "2", width: "12", height: "4", rx: "1" }), _jsx("rect", { x: "2", y: "10", width: "12", height: "4", rx: "1" }), _jsx("circle", { cx: "5", cy: "4", r: "0.75", fill: "currentColor", stroke: "none" }), _jsx("circle", { cx: "5", cy: "12", r: "0.75", fill: "currentColor", stroke: "none" })] }));
}
function XIcon() {
    return (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M4 4L10 10M10 4L4 10" }) }));
}
function ChevronRightIcon() {
    return (_jsx("svg", { width: "10", height: "10", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M6 4l4 4-4 4" }) }));
}
function SmallSpinner() {
    return (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", className: "shrink-0 animate-spin text-muted-foreground", "aria-hidden": "true", children: _jsx("path", { d: "M8 2a6 6 0 1 0 6 6" }) }));
}
//# sourceMappingURL=McpServerPicker.js.map