"use client";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { cn } from "@stigmer/theme";
import { getUserMessage, isRetryableError } from "@stigmer/sdk";
import { timestampDate } from "@bufbuild/protobuf/wkt";
import { OAuthConnectionHealth } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/io_pb";
import { ValidationState } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/status_pb";
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
import { useMcpServer } from "./useMcpServer";
import { useUpdateMcpServer } from "./useUpdateMcpServer";
import { mcpServerToInput } from "./internal/mcpServerToInput";
import { useMcpServerConnect } from "./useMcpServerConnect";
import { useMcpServerCredentials } from "./useMcpServerCredentials";
import { useMcpServerOAuthConnect } from "./useMcpServerOAuthConnect";
import { useDisconnectOAuth } from "./useDisconnectOAuth";
import { useOrgOAuthApp } from "./useOrgOAuthApp";
import { OAuthAppForm } from "./OAuthAppForm";
import { ErrorMessage } from "../error/ErrorMessage";
import { EnvVarForm } from "../environment/EnvVarForm";
import { VisibilityToggle } from "../library/VisibilityToggle";
import { PermissionGate } from "../iam-policy/PermissionGate";
import { Tabs } from "../tabs/Tabs";
import { ResourceDetailShell } from "../resource-detail/ResourceDetailShell";
import { Section } from "../resource-detail/Section";
import { InlineEditText } from "../inline-edit/InlineEditText";
import { InlineEditTextarea } from "../inline-edit/InlineEditTextarea";
import { InlineEditImage } from "../inline-edit/InlineEditImage";
import { InlineEditSelect } from "../inline-edit/InlineEditSelect";
import { InlineEditKeyValue } from "../inline-edit/InlineEditKeyValue";
/**
 * Detail view for an MCP Server integration.
 *
 * Fetches the server via {@link useMcpServer} internally and renders
 * its full configuration: validation banner (if invalid), header,
 * a **Connect bar** with a single Connect/Reconnect action, and a
 * **tabbed capabilities panel** (Tools, Policies, and optionally
 * Resources) showing read-only discovered data.
 *
 * The Connect bar above the tabs is the single entry point for
 * capability discovery and tool approval classification. It handles
 * credential gating inline — when credentials are missing, the form
 * slides in below the bar.
 *
 * Handles loading, error, and not-found states automatically.
 * Zero Console dependencies — safe for platform builder embedding.
 * All visual properties flow through `--stgm-*` design tokens.
 *
 * @example
 * ```tsx
 * // Minimal — self-contained, fetches its own data
 * <McpServerDetailView org="acme" slug="github" />
 * ```
 */
export function McpServerDetailView({ org, slug, onResourceLoad, onVisibilityChange, isVisibilityPending, defaultCapabilityTab = "tools", defaultShowCredentialForm = false, credentialPoolValues, activeOrg, primaryAction, actions, editable = false, onResourceUpdated, className, }) {
    const { mcpServer, isLoading, error, refetch } = useMcpServer(org, slug);
    const { update: updateMcpServer, isUpdating } = useUpdateMcpServer();
    const saveMcpField = useCallback(async (field, value) => {
        if (!mcpServer)
            return false;
        const input = mcpServerToInput(mcpServer);
        input[field] = value;
        try {
            const updated = await updateMcpServer(input);
            onResourceUpdated?.(updated);
            refetch();
            return true;
        }
        catch {
            return false;
        }
    }, [mcpServer, updateMcpServer, onResourceUpdated, refetch]);
    const credentials = useMcpServerCredentials(activeOrg ?? org, mcpServer ?? null);
    const connection = useMcpServerConnect();
    const oauth = useMcpServerOAuthConnect();
    const disconnectOAuth = useDisconnectOAuth();
    const orgOAuthApp = useOrgOAuthApp(mcpServer?.metadata?.id ?? null, activeOrg ?? org);
    const [showCredentialForm, setShowCredentialForm] = useState(defaultShowCredentialForm);
    const [showByoaForm, setShowByoaForm] = useState(false);
    const [capabilityTab, setCapabilityTab] = useState(defaultCapabilityTab);
    const byoaDialogRef = useRef(null);
    const onResourceLoadRef = useRef(onResourceLoad);
    onResourceLoadRef.current = onResourceLoad;
    useEffect(() => {
        if (mcpServer?.metadata?.name) {
            onResourceLoadRef.current?.({ name: mcpServer.metadata.name, id: mcpServer.metadata.id });
        }
    }, [mcpServer]);
    const handleOAuthSignIn = useCallback(async () => {
        if (!mcpServer?.metadata?.id)
            return;
        const envKeys = Object.keys(mcpServer.spec?.env ?? {});
        try {
            await oauth.startOAuth(mcpServer.metadata.id, activeOrg ?? org, envKeys);
            credentials.refetch();
            refetch();
        }
        catch {
            // error state is managed by the oauth hook
        }
    }, [mcpServer, oauth, credentials, refetch]);
    const handleConnectClick = useCallback(async () => {
        if (!mcpServer?.metadata?.id)
            return;
        if (credentials.authMode === "oauth" &&
            !credentials.isOAuthConnected &&
            !credentials.manualOverride) {
            handleOAuthSignIn();
            return;
        }
        if (!credentials.isReady) {
            setShowCredentialForm(true);
            return;
        }
        const envKeys = Object.keys(mcpServer.spec?.env ?? {});
        try {
            await connection.connect(mcpServer.metadata.id, activeOrg ?? org, undefined, envKeys);
            refetch();
        }
        catch {
            // error state is managed by the hook
        }
    }, [mcpServer, credentials.authMode, credentials.isOAuthConnected, credentials.manualOverride, credentials.isReady, connection, refetch, handleOAuthSignIn]);
    const handleCredentialSubmit = useCallback(async (values, options) => {
        try {
            if (options.saveForFuture) {
                await credentials.saveCredentials(values);
                credentials.refetch();
            }
            if (mcpServer?.metadata?.id) {
                const envKeys = Object.keys(mcpServer.spec?.env ?? {});
                const connectOrg = activeOrg ?? org;
                await connection.connect(mcpServer.metadata.id, connectOrg, values, envKeys);
                refetch();
            }
            setShowCredentialForm(false);
        }
        catch {
            // error state is managed by the hooks — form stays open for retry
        }
    }, [credentials, mcpServer, connection, refetch]);
    const handleDisconnect = useCallback(async () => {
        if (!mcpServer?.metadata?.id)
            return;
        try {
            await disconnectOAuth.disconnect(mcpServer.metadata.id, activeOrg ?? org);
            credentials.refetch();
            refetch();
        }
        catch {
            // error state is managed by the disconnect hook
        }
    }, [mcpServer, disconnectOAuth, credentials, refetch, activeOrg, org]);
    // BYOA dialog lifecycle — native <dialog> toggle
    useEffect(() => {
        const dialog = byoaDialogRef.current;
        if (!dialog)
            return;
        if (showByoaForm && !dialog.open) {
            dialog.showModal();
        }
        else if (!showByoaForm && dialog.open) {
            dialog.close();
        }
    }, [showByoaForm]);
    const handleByoaDialogCancel = useCallback((e) => {
        e.preventDefault();
        setShowByoaForm(false);
        orgOAuthApp.clearErrors();
    }, [orgOAuthApp]);
    const handleByoaSubmit = useCallback(async (clientId, clientSecret) => {
        await orgOAuthApp.setOrgOAuthApp(clientId, clientSecret);
        setShowByoaForm(false);
        orgOAuthApp.refetch();
        credentials.refetch();
        refetch();
    }, [orgOAuthApp, credentials, refetch]);
    const handleRemoveOrgApp = useCallback(async () => {
        await orgOAuthApp.deleteOrgOAuthApp();
        orgOAuthApp.refetch();
        credentials.refetch();
        refetch();
    }, [orgOAuthApp, credentials, refetch]);
    const spec = mcpServer?.spec;
    const status = mcpServer?.status;
    const hasSource = spec && (spec.repositoryUrl || spec.githubStars > 0);
    const specAudit = status?.audit?.specAudit;
    const capabilities = status?.discoveredCapabilities;
    const pinnedPolicies = spec?.pinnedToolApprovals ?? [];
    const classifiedPolicies = status?.toolApprovals ?? [];
    const totalPolicyCount = pinnedPolicies.length + classifiedPolicies.length;
    const tools = capabilities?.tools ?? [];
    const resourceTemplates = capabilities?.resourceTemplates ?? [];
    const hasDiscoveredTools = tools.length > 0;
    const capabilityTabs = useMemo(() => {
        const items = [
            { id: "tools", label: "Tools", badge: tools.length },
            { id: "policies", label: "Policies", badge: totalPolicyCount },
        ];
        if (resourceTemplates.length > 0) {
            items.push({
                id: "resources",
                label: "Resources",
                badge: resourceTemplates.length,
            });
        }
        return items;
    }, [tools.length, totalPolicyCount, resourceTemplates.length]);
    const combinedError = connection.error ?? oauth.error;
    const combinedClearError = useCallback(() => {
        connection.clearError();
        oauth.clearError();
    }, [connection, oauth]);
    if (isLoading)
        return _jsx(LoadingSkeleton, { className: className });
    if (error)
        return _jsx(ErrorMessage, { error: error, retry: refetch, className: className });
    if (!mcpServer)
        return _jsx(NotFoundState, { className: className });
    const meta = mcpServer.metadata;
    const lastDiscoveredAt = capabilities?.lastDiscoveredAt
        ? timestampDate(capabilities.lastDiscoveredAt)
        : null;
    const headerMeta = {
        name: meta?.name || meta?.slug || "Untitled",
        nameElement: editable && saveMcpField ? (_jsx(InlineEditText, { value: meta?.name || "", onSave: (v) => saveMcpField("name", v), isSaving: isUpdating, variant: "heading", placeholder: "Server name", validate: (v) => (v.trim() ? null : "Name is required") })) : undefined,
        id: meta?.id || "",
        org: meta?.org,
        slug: meta?.slug,
        qualifiedSlug: meta?.slug
            ? (meta.org ? `${meta.org}/${meta.slug}` : meta.slug)
            : undefined,
        icon: editable && saveMcpField ? (_jsx(InlineEditImage, { value: spec?.iconUrl ?? "", onSave: (v) => saveMcpField("iconUrl", v || undefined), isSaving: isUpdating, fallback: _jsx(McpServerIcon, { className: "size-6 text-muted-foreground" }), size: "md" })) : spec?.iconUrl ? undefined : _jsx(McpServerIcon, { className: "size-6 text-muted-foreground" }),
        iconUrl: editable ? undefined : spec?.iconUrl || undefined,
        createdAt: specAudit?.createdAt ? timestampDate(specAudit.createdAt) : null,
        updatedAt: specAudit?.updatedAt ? timestampDate(specAudit.updatedAt) : null,
    };
    const visibilityBadge = meta?.visibility === ApiResourceVisibility.visibility_public ? (_jsx("span", { className: "shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground", children: "Public" })) : undefined;
    const visibilityControl = onVisibilityChange && meta ? (_jsx(PermissionGate, { resource: { kind: "mcp_server", id: meta.id }, relation: "can_edit", fallback: visibilityBadge, children: _jsx(VisibilityToggle, { visibility: meta.visibility, onVisibilityChange: onVisibilityChange, isPending: isVisibilityPending }) })) : visibilityBadge;
    const headerMetaExtra = (_jsxs(_Fragment, { children: [status && _jsx(ValidationStateBadge, { state: status.validationState }), lastDiscoveredAt && (_jsxs(_Fragment, { children: [_jsx(Dot, {}), _jsxs("span", { children: ["Discovered ", formatDate(lastDiscoveredAt)] })] }))] }));
    const headerBanner = status?.validationState === ValidationState.invalid &&
        status.validationMessage ? (_jsx(ValidationBanner, { message: status.validationMessage })) : undefined;
    return (_jsxs(ResourceDetailShell, { header: headerMeta, visibilityControl: visibilityControl, headerMetaExtra: headerMetaExtra, headerBanner: headerBanner, primaryAction: primaryAction, actions: actions, className: className, children: [(editable || spec?.description) && (_jsx(Section, { title: "Description", children: editable && saveMcpField ? (_jsx("div", { className: "max-h-20 overflow-y-auto p-3", children: _jsx(InlineEditTextarea, { value: spec?.description || "", onSave: (v) => saveMcpField("description", v || undefined), isSaving: isUpdating, placeholder: "Add a description", minRows: 2 }) })) : (_jsx("div", { className: "p-3", children: _jsx("pre", { className: "whitespace-pre-wrap break-words text-sm text-foreground font-sans", children: spec?.description }) })) })), hasSource && _jsx(SourceSection, { spec: spec }), (editable || spec?.serverType.case) && (_jsx(ServerConfigSection, { serverType: spec?.serverType, editable: editable, isSaving: isUpdating, saveMcpField: saveMcpField })), (editable || (spec?.env && Object.keys(spec.env).length > 0)) && (_jsx(EnvSection, { data: spec?.env ?? {}, oauthTargetEnvVar: credentials.oauthTargetEnvVar, editable: editable, isSaving: isUpdating, saveMcpField: saveMcpField })), _jsxs(Section, { title: "Connection", children: [_jsx(ConnectBar, { isConnecting: connection.isConnecting || oauth.isInProgress, connectionError: combinedError, onConnect: handleConnectClick, onClearConnectionError: combinedClearError, hasDiscoveredTools: hasDiscoveredTools, credentialsLoading: credentials.isLoading, oauthPhase: oauth.phase, authMode: credentials.authMode, isOAuthConnected: credentials.isOAuthConnected, connectionHealth: credentials.connectionHealth, canDisconnect: credentials.canDisconnect, onDisconnect: handleDisconnect, isDisconnecting: disconnectOAuth.isDisconnecting, disconnectError: disconnectOAuth.error, onClearDisconnectError: disconnectOAuth.clearError, serverName: mcpServer?.metadata?.name ?? slug, accessTokenExpiresAt: credentials.accessTokenExpiresAt, tokenLifetimeHint: credentials.tokenLifetimeHint, isVendorApprovalPending: credentials.isVendorApprovalPending, isVendorApprovalBlocked: credentials.isVendorApprovalBlocked, vendorApprovalDocsUrl: credentials.vendorApprovalDocsUrl, canBringOwnApp: credentials.canBringOwnApp, isOrgOAuthApp: credentials.isOrgOAuthApp, onBringOwnApp: () => setShowByoaForm(true), onRemoveOrgApp: handleRemoveOrgApp, isRemovingOrgApp: orgOAuthApp.isDeleting, removeOrgAppError: orgOAuthApp.deleteError, onClearRemoveOrgAppError: orgOAuthApp.clearErrors, manualOverride: credentials.manualOverride, onManualOverride: () => {
                            credentials.setManualOverride(true);
                            setShowCredentialForm(true);
                        }, onBackToOAuth: () => {
                            credentials.setManualOverride(false);
                            setShowCredentialForm(false);
                        }, onCancelOAuth: oauth.clearError }), showCredentialForm && credentials.missingVariables.length > 0 && (_jsx("div", { className: "border-b border-border p-4", "data-cursor-target": "credential-form", children: _jsx(EnvVarForm, { title: "Credentials Required", description: "Enter the credentials needed to connect to this MCP server. Toggle \"Save for future runs\" to persist them in your personal environment, or leave it off for one-time use.", variables: credentials.missingVariables, onSubmit: (values, options) => handleCredentialSubmit(values, options), onCancel: () => setShowCredentialForm(false), isSubmitting: credentials.isSaving, poolValues: credentialPoolValues, className: "w-full max-w-md" }) }))] }), _jsxs("dialog", { ref: byoaDialogRef, onCancel: handleByoaDialogCancel, className: cn("m-auto w-full max-w-md rounded-lg border border-border bg-background p-6 shadow-lg", "backdrop:bg-black/50"), children: [_jsx("h3", { className: "mb-4 text-base font-semibold text-foreground", children: "Use your own OAuth app" }), _jsx(OAuthAppForm, { providerName: mcpServer?.metadata?.name ?? slug, vendorDocsUrl: credentials.vendorApprovalDocsUrl, onSubmit: handleByoaSubmit, onCancel: () => {
                            setShowByoaForm(false);
                            orgOAuthApp.clearErrors();
                        }, isSubmitting: orgOAuthApp.isSetting, error: orgOAuthApp.setError })] }), _jsx(Section, { title: "Capabilities", children: _jsxs(Tabs, { tabs: capabilityTabs, activeTab: capabilityTab, onTabChange: (id) => setCapabilityTab(id), "aria-label": "MCP server capabilities", children: [capabilityTab === "tools" && (_jsx(ToolsTabContent, { tools: tools })), capabilityTab === "policies" && (_jsx(PoliciesTabContent, { pinnedPolicies: pinnedPolicies, classifiedPolicies: classifiedPolicies, hasDiscoveredTools: hasDiscoveredTools })), capabilityTab === "resources" && (_jsx(ResourceTemplatesList, { templates: resourceTemplates }))] }) }), (editable || (spec && spec.tags.length > 0)) && (_jsx(TagsSection, { tags: spec?.tags ?? [], editable: editable, isSaving: isUpdating, saveMcpField: saveMcpField }))] }));
}
// ---------------------------------------------------------------------------
// ConnectBar — single entry point for capability discovery
// ---------------------------------------------------------------------------
/** Maps an OAuthConnectionHealth enum to pill display properties. */
function healthPillProps(health, isVendorApprovalPending) {
    if (isVendorApprovalPending) {
        return {
            pillClass: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
            dotClass: "bg-amber-500",
            label: "Pending approval",
        };
    }
    switch (health) {
        case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_HEALTHY:
            return {
                pillClass: "bg-success/10 text-success",
                dotClass: "bg-success",
                label: "Connected",
            };
        case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED_REFRESHABLE:
            return {
                pillClass: "bg-amber-500/10 text-amber-600 dark:text-amber-400",
                dotClass: "bg-amber-500",
                label: "Token expired",
            };
        case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED:
            return {
                pillClass: "bg-destructive-subtle text-destructive",
                dotClass: "bg-destructive",
                label: "Re-auth needed",
            };
        default:
            return {
                pillClass: "bg-muted text-muted-foreground",
                dotClass: "bg-muted-foreground",
                label: "Not connected",
            };
    }
}
/** Health-aware status detail text shown alongside the pill. */
function healthStatusText(health, accessTokenExpiresAt, tokenLifetimeHint) {
    switch (health) {
        case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_HEALTHY: {
            const expiryLabel = formatTokenExpiry(accessTokenExpiresAt);
            if (expiryLabel)
                return `Tokens refresh automatically \u00B7 ${expiryLabel}`;
            const hint = tokenLifetimeHint && tokenLifetimeHint !== "never"
                ? ` \u00B7 Session lasts ~${tokenLifetimeHint}`
                : "";
            return `Tokens refresh automatically${hint}`;
        }
        case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED_REFRESHABLE:
            return "Will refresh automatically on next use";
        case OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED:
            return "Token expired \u2014 sign in again to reconnect";
        default:
            return "Not connected yet";
    }
}
function ConnectBar({ isConnecting, connectionError, onConnect, onClearConnectionError, hasDiscoveredTools, credentialsLoading, oauthPhase, authMode, isOAuthConnected, connectionHealth, canDisconnect, onDisconnect, isDisconnecting, disconnectError, onClearDisconnectError, serverName, accessTokenExpiresAt, tokenLifetimeHint, isVendorApprovalPending, isVendorApprovalBlocked, vendorApprovalDocsUrl, canBringOwnApp, isOrgOAuthApp, onBringOwnApp, onRemoveOrgApp, isRemovingOrgApp, removeOrgAppError, onClearRemoveOrgAppError, manualOverride, onManualOverride, onBackToOAuth, onCancelOAuth, }) {
    const [disconnectPhase, setDisconnectPhase] = useState("idle");
    const [removeOrgAppPhase, setRemoveOrgAppPhase] = useState("idle");
    const isOAuthBusy = oauthPhase === "initiating" ||
        oauthPhase === "awaiting-callback" ||
        oauthPhase === "completing" ||
        oauthPhase === "connecting";
    const showOAuthPrimary = authMode === "oauth" && !isOAuthConnected && !manualOverride;
    const needsReAuth = connectionHealth === OAuthConnectionHealth.OAUTH_CONNECTION_HEALTH_TOKEN_EXPIRED;
    const oauthSignInDisabled = isVendorApprovalBlocked && showOAuthPrimary && !isOrgOAuthApp;
    const anyBusy = isConnecting || isOAuthBusy || isDisconnecting || isRemovingOrgApp;
    const buttonLabel = (() => {
        if (isOAuthBusy)
            return oauthPhaseLabel(oauthPhase);
        if (isConnecting)
            return "Connecting...";
        if (isOrgOAuthApp && showOAuthPrimary)
            return "Sign in with your app";
        if (showOAuthPrimary || needsReAuth)
            return "Sign in to connect";
        if (hasDiscoveredTools)
            return "Reconnect";
        return "Connect";
    })();
    const buttonIcon = (() => {
        if (isOAuthBusy || isConnecting)
            return _jsx(Spinner, {});
        if (showOAuthPrimary || needsReAuth)
            return _jsx(OAuthIcon, { className: "size-3.5" });
        if (hasDiscoveredTools)
            return _jsx(RefreshIcon, { className: "size-3.5" });
        return _jsx(ConnectIcon, { className: "size-3.5" });
    })();
    const statusText = (() => {
        if (authMode === "oauth" && isOAuthConnected) {
            const base = healthStatusText(connectionHealth, accessTokenExpiresAt, tokenLifetimeHint);
            return isOrgOAuthApp ? `${base} \u00B7 Using your OAuth app` : base;
        }
        if (isOrgOAuthApp && showOAuthPrimary)
            return "Using your OAuth app";
        if (manualOverride)
            return "Entering token manually";
        if (hasDiscoveredTools)
            return "Connected";
        return "Not connected yet";
    })();
    const pill = healthPillProps(connectionHealth, isVendorApprovalPending && !isOAuthConnected);
    const showDisconnectLink = canDisconnect && !anyBusy && !manualOverride && disconnectPhase === "idle";
    const showRemoveOrgAppLink = isOrgOAuthApp && !anyBusy && removeOrgAppPhase === "idle";
    // Inline disconnect confirmation replaces the main bar content
    if (disconnectPhase === "confirming" || disconnectPhase === "disconnecting") {
        return (_jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [_jsx(WarningIcon, { className: "size-3.5 shrink-0 text-destructive" }), _jsxs("p", { className: "flex-1 text-xs text-foreground", children: ["Remove OAuth credentials for ", _jsx("span", { className: "font-medium", children: serverName }), "? You can reconnect at any time."] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [_jsxs("button", { type: "button", disabled: isDisconnecting, onClick: async () => {
                                        setDisconnectPhase("disconnecting");
                                        try {
                                            await onDisconnect();
                                            setDisconnectPhase("idle");
                                        }
                                        catch {
                                            setDisconnectPhase("confirming");
                                        }
                                    }, className: cn("inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium", "bg-destructive text-destructive-foreground hover:bg-destructive-hover", "disabled:pointer-events-none disabled:opacity-50"), children: [isDisconnecting && _jsx(Spinner, {}), "Disconnect"] }), _jsx("button", { type: "button", disabled: isDisconnecting, onClick: () => {
                                        setDisconnectPhase("idle");
                                        onClearDisconnectError();
                                    }, className: cn("inline-flex items-center rounded-md px-2.5 py-1 text-xs font-medium", "border border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground", "disabled:pointer-events-none disabled:opacity-50"), children: "Cancel" })] })] }), disconnectError && (_jsxs("div", { className: "flex items-start gap-2 border-t border-destructive/20 bg-destructive-subtle px-3 py-2", children: [_jsx(WarningIcon, { className: "mt-0.5 size-3.5 shrink-0 text-destructive" }), _jsx("p", { className: "flex-1 text-xs text-destructive", children: getUserMessage(disconnectError) }), _jsx("button", { type: "button", onClick: onClearDisconnectError, className: "shrink-0 text-xs text-destructive-muted hover:text-destructive", "aria-label": "Dismiss error", children: "Dismiss" })] }))] }));
    }
    // Inline "remove custom app" confirmation
    if (removeOrgAppPhase === "confirming" || removeOrgAppPhase === "removing") {
        return (_jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [_jsx(WarningIcon, { className: "size-3.5 shrink-0 text-destructive" }), _jsxs("p", { className: "flex-1 text-xs text-foreground", children: ["Remove your custom OAuth app for", " ", _jsx("span", { className: "font-medium", children: serverName }), "? The server will revert to the platform's OAuth app."] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [_jsxs("button", { type: "button", disabled: isRemovingOrgApp, onClick: async () => {
                                        setRemoveOrgAppPhase("removing");
                                        try {
                                            await onRemoveOrgApp();
                                            setRemoveOrgAppPhase("idle");
                                        }
                                        catch {
                                            setRemoveOrgAppPhase("confirming");
                                        }
                                    }, className: cn("inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium", "bg-destructive text-destructive-foreground hover:bg-destructive-hover", "disabled:pointer-events-none disabled:opacity-50"), children: [isRemovingOrgApp && _jsx(Spinner, {}), "Remove"] }), _jsx("button", { type: "button", disabled: isRemovingOrgApp, onClick: () => {
                                        setRemoveOrgAppPhase("idle");
                                        onClearRemoveOrgAppError();
                                    }, className: cn("inline-flex items-center rounded-md px-2.5 py-1 text-xs font-medium", "border border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground", "disabled:pointer-events-none disabled:opacity-50"), children: "Cancel" })] })] }), removeOrgAppError && (_jsxs("div", { className: "flex items-start gap-2 border-t border-destructive/20 bg-destructive-subtle px-3 py-2", children: [_jsx(WarningIcon, { className: "mt-0.5 size-3.5 shrink-0 text-destructive" }), _jsx("p", { className: "flex-1 text-xs text-destructive", children: getUserMessage(removeOrgAppError) }), _jsx("button", { type: "button", onClick: onClearRemoveOrgAppError, className: "shrink-0 text-xs text-destructive-muted hover:text-destructive", "aria-label": "Dismiss error", children: "Dismiss" })] }))] }));
    }
    return (_jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center justify-between px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [authMode === "oauth" && !manualOverride && (_jsxs("span", { className: cn("inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-[10px] font-medium", pill.pillClass), children: [_jsx("span", { className: cn("size-1.5 rounded-full", pill.dotClass), "aria-hidden": "true" }), pill.label] })), _jsx("span", { className: "text-xs text-muted-foreground", children: oauthSignInDisabled ? "OAuth sign-in is pending vendor approval" : statusText }), showDisconnectLink && (_jsx("button", { type: "button", onClick: () => setDisconnectPhase("confirming"), className: "text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground", children: "Disconnect" })), showRemoveOrgAppLink && (_jsx("button", { type: "button", onClick: () => setRemoveOrgAppPhase("confirming"), className: "text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground", children: "Remove custom app" }))] }), _jsxs("button", { type: "button", onClick: onConnect, disabled: anyBusy || credentialsLoading || oauthSignInDisabled, "data-cursor-target": "connect-button", className: cn("inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium", showOAuthPrimary || needsReAuth
                            ? "bg-primary text-primary-foreground hover:bg-primary-hover"
                            : "border border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground", "disabled:pointer-events-none disabled:opacity-50"), children: [buttonIcon, buttonLabel] })] }), oauthPhase === "awaiting-callback" && (_jsx("div", { className: "flex items-center gap-3 border-t border-border px-3 py-1.5", children: _jsx("button", { type: "button", onClick: onCancelOAuth, className: "text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground", children: "Cancel sign-in" }) })), oauthSignInDisabled && (_jsxs("div", { className: "flex items-start gap-2 border-t border-amber-500/20 bg-amber-500/5 px-3 py-2", children: [_jsx(WarningIcon, { className: "mt-0.5 size-3.5 shrink-0 text-amber-600 dark:text-amber-400" }), _jsxs("div", { className: "flex-1 text-xs text-amber-700 dark:text-amber-300", children: [_jsxs("p", { children: ["The platform's OAuth app is awaiting vendor approval.", canBringOwnApp
                                        ? " You can use your own OAuth app or enter a token manually."
                                        : " You can still connect by entering your own token manually."] }), canBringOwnApp && (_jsx("button", { type: "button", onClick: onBringOwnApp, "data-cursor-target": "byoa-cta-button", className: "mt-1.5 inline-flex items-center gap-1 rounded-md bg-amber-600 px-2.5 py-1 text-[11px] font-medium text-white hover:bg-amber-700 dark:bg-amber-500 dark:text-amber-950 dark:hover:bg-amber-400", children: "Use your own OAuth app" })), vendorApprovalDocsUrl && !canBringOwnApp && (_jsxs("a", { href: vendorApprovalDocsUrl, target: "_blank", rel: "noopener noreferrer", className: "mt-1 inline-flex items-center gap-1 underline decoration-amber-600/40 underline-offset-2 hover:decoration-amber-600 dark:decoration-amber-400/40 dark:hover:decoration-amber-400", children: ["Learn how to bring your own token", _jsx(ExternalLinkIcon, { className: "size-3 shrink-0" })] }))] })] })), authMode === "oauth" && !isOAuthConnected && !isOAuthBusy && !isConnecting && (_jsx("div", { className: "flex items-center gap-3 border-t border-border px-3 py-1.5", children: manualOverride ? (_jsx("button", { type: "button", onClick: onBackToOAuth, className: "text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground", children: isVendorApprovalPending ? "Back to OAuth status" : "Sign in with OAuth instead" })) : (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: onManualOverride, className: "text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground", children: "Enter token manually" }), canBringOwnApp && !isVendorApprovalBlocked && (_jsx("button", { type: "button", onClick: onBringOwnApp, className: "text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground", children: "Use your own OAuth app" }))] })) })), connectionError && (_jsxs("div", { className: "flex items-start gap-2 border-t border-destructive/20 bg-destructive-subtle px-3 py-2", role: "alert", children: [_jsx(WarningIcon, { className: "mt-0.5 size-3.5 shrink-0 text-destructive" }), _jsx("p", { className: "flex-1 text-xs text-destructive", children: getUserMessage(connectionError) }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [isRetryableError(connectionError) && (_jsx("button", { type: "button", onClick: () => {
                                    onClearConnectionError();
                                    onConnect();
                                }, className: "text-xs font-medium text-destructive underline underline-offset-2 hover:no-underline", children: "Try again" })), _jsx("button", { type: "button", onClick: onClearConnectionError, className: "text-xs text-destructive-muted hover:text-destructive", "aria-label": "Dismiss error", children: "Dismiss" })] })] }))] }));
}
function oauthPhaseLabel(phase) {
    switch (phase) {
        case "initiating":
            return "Starting sign-in...";
        case "awaiting-callback":
            return "Waiting for authorization...";
        case "completing":
            return "Completing sign-in...";
        case "connecting":
            return "Discovering tools...";
        default:
            return "Connecting...";
    }
}
function formatTokenExpiry(expiresAtSeconds) {
    if (expiresAtSeconds === BigInt(0))
        return null;
    const nowSeconds = BigInt(Math.floor(Date.now() / 1000));
    const remainingSeconds = expiresAtSeconds - nowSeconds;
    if (remainingSeconds <= BigInt(0))
        return "Token expired";
    const minutes = Number(remainingSeconds / BigInt(60));
    if (minutes < 1)
        return "Expires in <1 min";
    if (minutes < 60)
        return `Expires in ${minutes} min`;
    const hours = Math.floor(minutes / 60);
    if (hours < 24)
        return `Expires in ${hours}h ${minutes % 60}m`;
    const days = Math.floor(hours / 24);
    return `Expires in ${days}d`;
}
// ---------------------------------------------------------------------------
// Internal section components
// ---------------------------------------------------------------------------
function ValidationBanner({ message }) {
    return (_jsxs("div", { role: "alert", className: "flex items-start gap-2.5 rounded-lg border border-destructive/30 bg-destructive-subtle px-4 py-3", children: [_jsx(WarningIcon, { className: "mt-0.5 size-4 shrink-0 text-destructive" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-sm font-medium text-destructive", children: "Invalid Configuration" }), _jsx("p", { className: "mt-0.5 text-xs text-destructive-muted", children: message })] })] }));
}
function ValidationStateBadge({ state, }) {
    switch (state) {
        case ValidationState.valid:
            return (_jsxs(_Fragment, { children: [_jsx(Dot, {}), _jsxs("span", { className: "inline-flex items-center gap-1 text-emerald-600 dark:text-emerald-400", children: [_jsx(CheckIcon, { className: "size-3" }), "Valid"] })] }));
        case ValidationState.invalid:
            return (_jsxs(_Fragment, { children: [_jsx(Dot, {}), _jsx("span", { className: "text-destructive", children: "Invalid" })] }));
        default:
            return null;
    }
}
const TRANSPORT_OPTIONS = [
    { value: "http", label: "HTTP", description: "Connect via HTTP/SSE endpoint" },
    { value: "stdio", label: "Stdio", description: "Launch a local process with stdin/stdout" },
];
function ServerConfigSection({ serverType, editable, isSaving, saveMcpField, }) {
    const [headersEditing, setHeadersEditing] = useState(false);
    const [queryParamsEditing, setQueryParamsEditing] = useState(false);
    const currentHttpConfig = useMemo(() => {
        if (serverType?.case !== "http")
            return null;
        const v = serverType.value;
        return {
            url: v.url,
            headers: v.headers && Object.keys(v.headers).length > 0 ? { ...v.headers } : undefined,
            queryParams: v.queryParams && Object.keys(v.queryParams).length > 0 ? { ...v.queryParams } : undefined,
            timeoutSeconds: v.timeoutSeconds || undefined,
        };
    }, [serverType]);
    const handleTransportChange = useCallback(async (newType) => {
        if (!saveMcpField)
            return false;
        if (newType === "http") {
            const ok = await saveMcpField("http", { url: "" });
            if (ok)
                await saveMcpField("stdio", undefined);
            return ok;
        }
        const ok = await saveMcpField("stdio", { command: "" });
        if (ok)
            await saveMcpField("http", undefined);
        return ok;
    }, [saveMcpField]);
    const headerRows = useMemo(() => {
        if (!currentHttpConfig?.headers)
            return [];
        return Object.entries(currentHttpConfig.headers).map(([key, value]) => ({ key, value }));
    }, [currentHttpConfig?.headers]);
    const queryParamRows = useMemo(() => {
        if (!currentHttpConfig?.queryParams)
            return [];
        return Object.entries(currentHttpConfig.queryParams).map(([key, value]) => ({ key, value }));
    }, [currentHttpConfig?.queryParams]);
    const handleHeadersSave = useCallback(async (rows) => {
        if (!saveMcpField || !currentHttpConfig)
            return false;
        const headers = {};
        for (const row of rows) {
            if (row.key.trim())
                headers[row.key.trim()] = row.value;
        }
        return saveMcpField("http", {
            ...currentHttpConfig,
            headers: Object.keys(headers).length > 0 ? headers : undefined,
        });
    }, [saveMcpField, currentHttpConfig]);
    const handleQueryParamsSave = useCallback(async (rows) => {
        if (!saveMcpField || !currentHttpConfig)
            return false;
        const queryParams = {};
        for (const row of rows) {
            if (row.key.trim())
                queryParams[row.key.trim()] = row.value;
        }
        return saveMcpField("http", {
            ...currentHttpConfig,
            queryParams: Object.keys(queryParams).length > 0 ? queryParams : undefined,
        });
    }, [saveMcpField, currentHttpConfig]);
    return (_jsxs(Section, { title: "Server Configuration", children: [_jsxs("div", { className: "flex flex-col gap-2 p-3", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Type" }), editable && saveMcpField ? (_jsx(InlineEditSelect, { value: serverType?.case ?? "http", options: TRANSPORT_OPTIONS, onSave: handleTransportChange, isSaving: isSaving })) : (_jsx("span", { className: "rounded bg-muted px-1.5 py-0.5 font-mono text-xs font-medium text-foreground", children: serverType?.case ?? "none" }))] }), serverType?.case === "stdio" && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Command" }), editable && saveMcpField ? (_jsx(InlineEditText, { value: `${serverType.value.command}${serverType.value.args.length > 0 ? ` ${serverType.value.args.join(" ")}` : ""}`, onSave: async (v) => {
                                            const parts = v.trim().split(/\s+/);
                                            return saveMcpField("stdio", {
                                                command: parts[0] || "",
                                                args: parts.slice(1),
                                            });
                                        }, isSaving: isSaving, placeholder: "e.g. npx -y @modelcontextprotocol/server" })) : (_jsxs("code", { className: "font-mono text-sm text-foreground", children: [serverType.value.command, serverType.value.args.length > 0 &&
                                                ` ${serverType.value.args.join(" ")}`] }))] }), (editable || serverType.value.workingDir) && (_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Working Dir" }), editable && saveMcpField ? (_jsx(InlineEditText, { value: serverType.value.workingDir ?? "", onSave: async (v) => saveMcpField("stdio", {
                                            command: serverType.value.command,
                                            args: [...serverType.value.args],
                                            workingDir: v || undefined,
                                        }), isSaving: isSaving, placeholder: "/path/to/working/dir" })) : (_jsx("code", { className: "font-mono text-xs text-foreground", children: serverType.value.workingDir }))] }))] })), serverType?.case === "http" && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "URL" }), editable && saveMcpField ? (_jsx(InlineEditText, { value: serverType.value.url, onSave: async (v) => saveMcpField("http", { ...currentHttpConfig, url: v }), isSaving: isSaving, placeholder: "https://example.com/mcp" })) : (_jsx("code", { className: "break-all font-mono text-sm text-foreground", children: serverType.value.url }))] }), (editable || serverType.value.timeoutSeconds > 0) && (_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Timeout" }), editable && saveMcpField ? (_jsx(InlineEditText, { value: serverType.value.timeoutSeconds > 0 ? String(serverType.value.timeoutSeconds) : "", onSave: async (v) => saveMcpField("http", {
                                            ...currentHttpConfig,
                                            timeoutSeconds: v ? Number(v) : undefined,
                                        }), isSaving: isSaving, placeholder: "30", validate: (v) => {
                                            if (v && (isNaN(Number(v)) || Number(v) < 0))
                                                return "Must be a positive number";
                                            return null;
                                        } })) : (_jsxs("span", { className: "text-xs text-foreground", children: [serverType.value.timeoutSeconds, "s"] }))] }))] }))] }), serverType?.case === "http" && (editable || headerRows.length > 0) && (_jsx(HttpKeyValueSubsection, { title: "Headers", count: headerRows.length, rows: headerRows, editable: editable, isSaving: isSaving, editing: headersEditing, onEditingChange: setHeadersEditing, onSave: handleHeadersSave, keyLabel: "Header name" })), serverType?.case === "http" && (editable || queryParamRows.length > 0) && (_jsx(HttpKeyValueSubsection, { title: "Query Parameters", count: queryParamRows.length, rows: queryParamRows, editable: editable, isSaving: isSaving, editing: queryParamsEditing, onEditingChange: setQueryParamsEditing, onSave: handleQueryParamsSave, keyLabel: "Parameter name" }))] }));
}
/** Renders a key-value subsection (headers or query params) within ServerConfigSection. */
function HttpKeyValueSubsection({ title, count, rows, editable, isSaving, editing, onEditingChange, onSave, keyLabel, }) {
    return (_jsxs("div", { className: "border-t border-border", children: [_jsxs("div", { className: "flex items-center justify-between px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }), count > 0 && (_jsx("span", { className: "inline-flex min-w-[1.25rem] items-center justify-center rounded-full bg-muted px-1 py-px text-[10px] font-medium leading-none text-muted-foreground", children: count }))] }), editable && (_jsx("button", { type: "button", onClick: () => onEditingChange(!editing), className: "text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground", children: editing ? "Done" : "Edit" }))] }), editable && editing ? (_jsx(InlineEditKeyValue, { value: [...rows], onSave: onSave, isSaving: isSaving, editing: editing, onEditingChange: onEditingChange, keyLabel: keyLabel, showValue: true, valueLabel: "Value" })) : (_jsx("div", { className: "flex flex-col divide-y divide-border", children: rows.map((row) => (_jsxs("div", { className: "flex items-start gap-2 px-3 py-1.5", children: [_jsx("code", { className: "shrink-0 font-mono text-xs font-medium text-foreground", children: row.key }), _jsx("span", { className: "min-w-0 break-all font-mono text-xs text-muted-foreground", children: renderHeaderValue(row.value) })] }, row.key))) }))] }));
}
const ENV_VAR_PLACEHOLDER = /\$\{([^}]+)\}/g;
/** Renders a header value, highlighting ${VAR} placeholders with a variable badge. */
function renderHeaderValue(value) {
    if (!ENV_VAR_PLACEHOLDER.test(value))
        return value;
    ENV_VAR_PLACEHOLDER.lastIndex = 0;
    const parts = [];
    let lastIndex = 0;
    let match;
    while ((match = ENV_VAR_PLACEHOLDER.exec(value)) !== null) {
        if (match.index > lastIndex) {
            parts.push(value.slice(lastIndex, match.index));
        }
        parts.push(_jsx("span", { className: "inline-flex items-center gap-0.5 rounded bg-primary-subtle px-1 py-px text-[10px] font-medium text-primary", title: `Resolved from environment variable: ${match[1]}`, children: match[0] }, match.index));
        lastIndex = match.index + match[0].length;
    }
    if (lastIndex < value.length) {
        parts.push(value.slice(lastIndex));
    }
    return _jsx(_Fragment, { children: parts });
}
function SourceSection({ spec, }) {
    return (_jsx(Section, { title: "Source", children: _jsxs("div", { className: "flex flex-col gap-2 p-3", children: [spec.repositoryUrl && (_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "shrink-0 text-xs font-medium text-muted-foreground", children: "Repository" }), _jsxs("a", { href: spec.repositoryUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 break-all font-mono text-xs text-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:decoration-foreground", children: [spec.repositoryUrl, _jsx(ExternalLinkIcon, { className: "size-3 shrink-0" })] })] })), spec.githubStars > 0 && (_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "shrink-0 text-xs font-medium text-muted-foreground", children: "Stars" }), _jsx("span", { className: "text-xs text-foreground", children: spec.githubStars.toLocaleString() })] }))] }) }));
}
function ResourceTemplatesList({ templates, }) {
    if (templates.length === 0)
        return null;
    return (_jsx("div", { className: "flex flex-col divide-y divide-border", children: templates.map((tpl) => (_jsxs("div", { className: "px-3 py-2.5", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-sm font-medium text-foreground", children: tpl.name }), _jsx("code", { className: "font-mono text-[10px] text-muted-foreground", children: tpl.uriTemplate })] }), tpl.description && (_jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: tpl.description }))] }, tpl.uriTemplate || tpl.name))) }));
}
function EnvSection({ data, oauthTargetEnvVar, editable, isSaving, saveMcpField, }) {
    const entries = Object.entries(data).sort(([a], [b]) => a.localeCompare(b));
    const envRows = useMemo(() => entries.map(([key, decl]) => ({
        key,
        value: "",
        isSecret: decl.isSecret,
        description: decl.description,
        optional: decl.optional,
    })), [entries]);
    const handleEnvSave = useCallback(async (rows) => {
        if (!saveMcpField)
            return false;
        const env = {};
        for (const row of rows) {
            if (row.key.trim()) {
                env[row.key.trim()] = {
                    isSecret: row.isSecret || undefined,
                    description: row.description || undefined,
                    optional: row.optional || undefined,
                };
            }
        }
        return saveMcpField("env", Object.keys(env).length > 0 ? env : undefined);
    }, [saveMcpField]);
    const [envEditing, setEnvEditing] = useState(false);
    return (_jsx(Section, { title: "Environment Variables", count: entries.length, onEdit: editable ? () => setEnvEditing((v) => !v) : undefined, children: editable ? (_jsx(InlineEditKeyValue, { value: envRows, onSave: handleEnvSave, isSaving: isSaving, editing: envEditing, onEditingChange: setEnvEditing, showSecretToggle: true, showOptionalToggle: true, showDescription: true, keyLabel: "Variable name" })) : (_jsx("div", { className: "flex flex-col divide-y divide-border", children: entries.map(([name, env]) => {
                const isOAuthManaged = name === oauthTargetEnvVar;
                return (_jsxs("div", { className: "flex items-start gap-3 px-3 py-2", children: [_jsx("code", { className: "shrink-0 font-mono text-sm font-medium text-foreground", children: name }), _jsx("span", { className: "shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground", children: env.isSecret ? "secret" : "config" }), isOAuthManaged && (_jsx("span", { className: "shrink-0 rounded bg-primary-subtle px-1.5 py-0.5 text-[10px] font-medium text-primary", children: "oauth" })), env.optional && (_jsx("span", { className: "shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground-subtle", children: "optional" })), env.description && (_jsx("span", { className: "text-xs text-muted-foreground", children: env.description }))] }, name));
            }) })) }));
}
function TagsSection({ tags, editable, isSaving, saveMcpField, }) {
    const tagRows = useMemo(() => tags.map((t) => ({ key: t, value: "" })), [tags]);
    const handleTagsSave = useCallback(async (rows) => {
        if (!saveMcpField)
            return false;
        // Tags are stored as string[] on the spec but not directly on McpServerInput.
        // For now, save them through the full input by modifying the spec.
        // Tags don't have a direct field on McpServerInput, so we handle this at
        // a higher level if needed. For now, show read-only in edit mode.
        return false;
    }, [saveMcpField]);
    return (_jsx(Section, { title: "Tags", count: tags.length, children: _jsxs("div", { className: "flex flex-wrap gap-1.5 p-3", children: [tags.map((tag) => (_jsx("span", { className: "rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground", children: tag }, tag))), editable && tags.length === 0 && (_jsx("p", { className: "text-xs text-muted-foreground italic", children: "No tags" }))] }) }));
}
// ---------------------------------------------------------------------------
// Capability tab contents (read-only)
// ---------------------------------------------------------------------------
function ToolsTabContent({ tools, }) {
    const [search, setSearch] = useState("");
    const [expandedTool, setExpandedTool] = useState(null);
    const filtered = useMemo(() => {
        if (!search.trim())
            return tools;
        const q = search.toLowerCase();
        return tools.filter((t) => t.name.toLowerCase().includes(q) ||
            t.description?.toLowerCase().includes(q));
    }, [tools, search]);
    if (tools.length === 0) {
        return (_jsxs("div", { className: "px-3 py-8 text-center", children: [_jsx(ConnectIcon, { className: "mx-auto mb-2 size-6 text-muted-foreground-faint" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Connect to this MCP server to discover its available tools." })] }));
    }
    const isFiltered = search.trim().length > 0;
    return (_jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 pb-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(SearchIcon, { className: "pointer-events-none absolute left-2 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { type: "text", value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Search tools\u2026", "aria-label": "Search tools", className: "w-full rounded-md border border-border bg-background py-1.5 pl-7 pr-7 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" }), isFiltered && (_jsx("button", { type: "button", onClick: () => setSearch(""), "aria-label": "Clear search", className: "absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground", children: _jsx(CloseIcon, { className: "size-3" }) }))] }), _jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: isFiltered ? `${filtered.length} of ${tools.length}` : tools.length })] }), filtered.length === 0 ? (_jsx("div", { className: "px-3 py-6 text-center", children: _jsxs("p", { className: "text-xs text-muted-foreground", children: ["No tools matching \u201C", search, "\u201D"] }) })) : (_jsx("div", { className: "max-h-96 overflow-y-auto", children: _jsx("div", { className: "flex flex-col divide-y divide-border", children: filtered.map((tool) => {
                        const isExpanded = expandedTool === tool.name;
                        const hasSchema = tool.inputSchema != null &&
                            Object.keys(tool.inputSchema).length > 0;
                        return (_jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setExpandedTool(isExpanded ? null : tool.name), className: "flex w-full items-start gap-2 px-3 py-2.5 text-left transition-colors hover:bg-muted-faint", "aria-expanded": isExpanded, children: [_jsx(ChevronIcon, { className: cn("mt-0.5 size-3 shrink-0 text-muted-foreground transition-transform", isExpanded && "rotate-90") }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("code", { className: "font-mono text-sm font-medium text-foreground", children: tool.name }), tool.description && (_jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: tool.description }))] }), hasSchema && (_jsx("span", { className: "mt-0.5 shrink-0 rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground", children: "schema" }))] }), isExpanded && hasSchema && (_jsx("div", { className: "border-t border-border bg-muted-faint px-3 py-2", children: _jsx("pre", { className: "max-h-64 overflow-auto whitespace-pre-wrap break-words rounded border border-border bg-background p-2 font-mono text-[11px] text-foreground", children: JSON.stringify(tool.inputSchema, null, 2) }) }))] }, tool.name));
                    }) }) }))] }));
}
function PoliciesTabContent({ pinnedPolicies, classifiedPolicies, hasDiscoveredTools, }) {
    const [search, setSearch] = useState("");
    const totalCount = pinnedPolicies.length + classifiedPolicies.length;
    const hasAnyPolicies = totalCount > 0;
    const filteredPinned = useMemo(() => {
        if (!search.trim())
            return pinnedPolicies;
        const q = search.toLowerCase();
        return pinnedPolicies.filter((p) => p.toolName.toLowerCase().includes(q) ||
            p.message?.toLowerCase().includes(q));
    }, [pinnedPolicies, search]);
    const filteredClassified = useMemo(() => {
        if (!search.trim())
            return classifiedPolicies;
        const q = search.toLowerCase();
        return classifiedPolicies.filter((p) => p.toolName.toLowerCase().includes(q) ||
            p.message?.toLowerCase().includes(q));
    }, [classifiedPolicies, search]);
    const filteredTotal = filteredPinned.length + filteredClassified.length;
    const isFiltered = search.trim().length > 0;
    if (!hasAnyPolicies) {
        return (_jsxs("div", { className: "px-3 py-8 text-center", children: [_jsx(ShieldIcon, { className: "mx-auto mb-2 size-6 text-muted-foreground-faint" }), _jsx("p", { className: "text-xs text-muted-foreground", children: hasDiscoveredTools
                        ? "No approval policies yet. Reconnect to reclassify tools."
                        : "Connect to discover tools and auto-classify approval policies." })] }));
    }
    return (_jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 pb-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(SearchIcon, { className: "pointer-events-none absolute left-2 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { type: "text", value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Search policies\u2026", "aria-label": "Search policies", className: "w-full rounded-md border border-border bg-background py-1.5 pl-7 pr-7 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring" }), isFiltered && (_jsx("button", { type: "button", onClick: () => setSearch(""), "aria-label": "Clear search", className: "absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground", children: _jsx(CloseIcon, { className: "size-3" }) }))] }), _jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: isFiltered ? `${filteredTotal} of ${totalCount}` : totalCount })] }), filteredTotal === 0 ? (_jsx("div", { className: "px-3 py-6 text-center", children: _jsxs("p", { className: "text-xs text-muted-foreground", children: ["No policies matching \u201C", search, "\u201D"] }) })) : (_jsxs("div", { className: "max-h-96 overflow-y-auto", children: [filteredPinned.length > 0 && (_jsx(PolicyGroup, { icon: _jsx(PinIcon, { className: "size-3.5" }), label: "Pinned", policies: filteredPinned })), filteredClassified.length > 0 && (_jsx(PolicyGroup, { icon: _jsx(SparklesIcon, { className: "size-3.5" }), label: "Auto-classified", policies: filteredClassified }))] }))] }));
}
function PolicyGroup({ icon, label, policies, }) {
    return (_jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-1.5 border-b border-border bg-muted-faint px-3 py-1.5", children: [_jsx("span", { className: "text-muted-foreground", children: icon }), _jsx("span", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: label }), _jsxs("span", { className: "text-[10px] text-muted-foreground", children: ["(", policies.length, ")"] })] }), _jsx("div", { className: "flex flex-col divide-y divide-border", children: policies.map((policy) => (_jsxs("div", { className: "px-3 py-2.5", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("code", { className: "font-mono text-sm font-medium text-foreground", children: policy.toolName }), _jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-amber-500/10 px-1.5 py-0.5 text-[10px] font-medium text-amber-600 dark:text-amber-400", children: [_jsx(ShieldIcon, { className: "size-2.5" }), "requires approval"] })] }), policy.message && (_jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: policy.message }))] }, policy.toolName))) })] }));
}
// ---------------------------------------------------------------------------
// Shared layout primitives
// ---------------------------------------------------------------------------
function Dot() {
    return (_jsx("span", { className: "shrink-0", "aria-hidden": "true", children: "\u00B7" }));
}
// ---------------------------------------------------------------------------
// Non-happy states
// ---------------------------------------------------------------------------
function LoadingSkeleton({ className }) {
    return (_jsxs("div", { className: cn("flex flex-col gap-6", className), "aria-busy": "true", "aria-label": "Loading MCP server details", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "mt-1 size-6 shrink-0 animate-pulse rounded bg-muted" }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx("div", { className: "h-5 w-48 animate-pulse rounded bg-muted" }), _jsx("div", { className: "h-3 w-64 animate-pulse rounded bg-muted" }), _jsx("div", { className: "h-4 w-full max-w-md animate-pulse rounded bg-muted" })] })] }), [24, 48, 16, 12].map((h, i) => (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "h-3 w-28 animate-pulse rounded bg-muted" }), _jsx("div", { className: "animate-pulse rounded-lg border border-border bg-muted-faint", style: { height: `${h * 4}px` } })] }, i)))] }));
}
function NotFoundState({ className }) {
    return (_jsxs("div", { role: "status", className: cn("flex flex-col items-center gap-2 py-12 text-center", className), children: [_jsx(McpServerIcon, { className: "size-10 text-muted-foreground-faint" }), _jsx("p", { className: "text-sm font-medium text-muted-foreground", children: "MCP Server not found" }), _jsx("p", { className: "text-xs text-muted-foreground-subtle", children: "This MCP server doesn't exist or you don't have access to it." })] }));
}
// ---------------------------------------------------------------------------
// Utilities
// ---------------------------------------------------------------------------
function formatDate(date) {
    return date.toLocaleDateString("en-US", {
        month: "short",
        day: "numeric",
        year: "numeric",
    });
}
// ---------------------------------------------------------------------------
// Icons — inline SVGs following the SDK pattern (no icon library dependency)
// ---------------------------------------------------------------------------
function McpServerIcon({ className }) {
    return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("rect", { x: "2", y: "2", width: "12", height: "5", rx: "1" }), _jsx("rect", { x: "2", y: "9", width: "12", height: "5", rx: "1" }), _jsx("circle", { cx: "5", cy: "4.5", r: "0.75", fill: "currentColor", stroke: "none" }), _jsx("circle", { cx: "5", cy: "11.5", r: "0.75", fill: "currentColor", stroke: "none" })] }));
}
function CheckIcon({ className }) {
    return (_jsx("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m3 8.5 3.5 3.5 6.5-8" }) }));
}
function WarningIcon({ className }) {
    return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M8 1.5 1 14h14L8 1.5Z" }), _jsx("path", { d: "M8 6v3.5" }), _jsx("circle", { cx: "8", cy: "11.5", r: "0.5", fill: "currentColor", stroke: "none" })] }));
}
function ConnectIcon({ className }) {
    return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M6.5 6.5 4.25 4.25" }), _jsx("path", { d: "m9.5 9.5 2.25 2.25" }), _jsx("rect", { x: "1.5", y: "1.5", width: "5", height: "5", rx: "1" }), _jsx("rect", { x: "9.5", y: "9.5", width: "5", height: "5", rx: "1" }), _jsx("path", { d: "m6.5 9.5-3 3" }), _jsx("path", { d: "m9.5 6.5 3-3" })] }));
}
function RefreshIcon({ className }) {
    return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M2.5 8a5.5 5.5 0 0 1 9.36-3.92" }), _jsx("path", { d: "M13.5 8a5.5 5.5 0 0 1-9.36 3.92" }), _jsx("path", { d: "M12 2v3h-3" }), _jsx("path", { d: "M4 14v-3h3" })] }));
}
function ShieldIcon({ className }) {
    return (_jsx("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M8 1.5 2.5 4v4c0 3.31 2.35 6.4 5.5 7 3.15-.6 5.5-3.69 5.5-7V4L8 1.5Z" }) }));
}
function PinIcon({ className }) {
    return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M9.5 2.5 13.5 6.5" }), _jsx("path", { d: "M5 7 3 14l7-2" }), _jsx("path", { d: "m5 7 2-2 4.5-1 1.5 1.5-1 4.5-2 2z" })] }));
}
function SparklesIcon({ className }) {
    return (_jsx("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M8 1v2M8 13v2M1 8h2M13 8h2M3.05 3.05l1.41 1.41M11.54 11.54l1.41 1.41M3.05 12.95l1.41-1.41M11.54 4.46l1.41-1.41" }) }));
}
function OAuthIcon({ className }) {
    return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("rect", { x: "1.5", y: "5", width: "13", height: "8", rx: "1.5" }), _jsx("path", { d: "M4.5 5V3.5a3.5 3.5 0 0 1 7 0V5" }), _jsx("circle", { cx: "8", cy: "9.5", r: "1.25" })] }));
}
function ExternalLinkIcon({ className }) {
    return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M12 8.667v4A1.333 1.333 0 0 1 10.667 14H3.333A1.333 1.333 0 0 1 2 12.667V5.333A1.333 1.333 0 0 1 3.333 4h4" }), _jsx("path", { d: "M10 2h4v4" }), _jsx("path", { d: "M6.667 9.333 14 2" })] }));
}
function SearchIcon({ className }) {
    return (_jsxs("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("circle", { cx: "7", cy: "7", r: "4.5" }), _jsx("path", { d: "m10.5 10.5 3 3" })] }));
}
function CloseIcon({ className }) {
    return (_jsx("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m4 4 8 8M12 4l-8 8" }) }));
}
function ChevronIcon({ className }) {
    return (_jsx("svg", { className: className, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m6 4 4 4-4 4" }) }));
}
function Spinner() {
    return (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", className: "animate-spin", "aria-hidden": "true", children: _jsx("path", { d: "M8 2a6 6 0 1 0 6 6" }) }));
}
//# sourceMappingURL=McpServerDetailView.js.map