"use client";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { cn } from "@stigmer/theme";
import { getUserMessage } from "@stigmer/sdk";
import { useComposer } from "./useComposer";
import { ComposerToolbar } from "./ComposerToolbar";
import { parseModelKey } from "../models/registry";
import { ContextChip } from "./ContextChip";
import { WorkspaceEditor } from "../workspace/WorkspaceEditor";
import { AgentPicker } from "../agent/AgentPicker";
import { AgentEnvForm } from "../agent/AgentEnvForm";
import { useAgentSetup } from "../agent/useAgentSetup";
import { SecretFlowErrorGuide, isSecretFlowError } from "../error/SecretFlowErrorGuide";
import { McpServerPicker } from "../mcp-server/McpServerPicker";
import { useMcpServerSetup } from "../mcp-server/useMcpServerSetup";
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
import { SkillPicker } from "../skill/SkillPicker";
import { SessionVariablesInput } from "../execution/SessionVariablesInput";
import { useRunnerList } from "../runner/useRunnerList";
import { WorkspaceRunnerSelector } from "../runner/WorkspaceRunnerSelector";
import { useAttachments } from "../attachment/useAttachments";
import { AttachmentChipList } from "../attachment/AttachmentChipList";
import { useSessionEnvPool } from "../environment/useSessionEnvPool";
import { usePersonalEnvironment } from "../environment/usePersonalEnvironment";
import { useStigmer } from "../hooks";
import { SYSTEM_ENV_VAR_KEYS, resolveSystemEnvVarValues, } from "../environment/systemEnvVars";
import { useRenderTracer } from "../internal/dev";
import { RunnerPhase } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/enum_pb";
import { isActivePhase, phaseLabel, phaseDotColor, PHASE_SORT_ORDER, } from "../runner/phase";
import { AgentIcon, McpServerIcon, SkillIcon, SecretsIcon, RunnerIcon, AlertTriangleIcon, ResolveSpinner, } from "./icons";
/**
 * Unified message composer for Stigmer sessions.
 *
 * Combines a self-resizing textarea, model selector, and context pickers
 * (agent, workspace, MCP servers, skills) into a single input card.
 *
 * The toolbar uses a two-tier layout:
 * - **Tier 1** (always visible): Workspace, Attach, Model Selector
 * - **Tier 2** (behind Configure menu): Agent, MCP, Skills, Secrets
 *
 * Selected items render as removable chips between the textarea and toolbar.
 *
 * Used for both new session creation (launcher) and follow-up messages
 * within an existing session. Layout positioning is the consumer's
 * responsibility.
 *
 * Uses `<div role="form">` instead of `<form>` so it can be embedded
 * inside host application forms without nesting violations.
 *
 * All visual properties flow through `--stgm-*` tokens.
 *
 * @example
 * ```tsx
 * // Launcher with all context types
 * <SessionComposer
 *   onSubmit={handleCreate}
 *   org={org}
 *   agentRef={agentRef}
 *   onAgentRefChange={setAgentRef}
 *   onAgentResolutionChange={setResolution}
 *   workspace={workspace}
 *   enableGitHub
 *   mcpServerUsages={mcpUsages}
 *   onMcpServerUsagesChange={setMcpUsages}
 *   skillRefs={skillRefs}
 *   onSkillRefsChange={setSkillRefs}
 *   initialRows={3}
 *   placeholder="Describe what you need help with..."
 *   autoFocus
 * />
 *
 * // Pre-filled launcher (auto-selects agent on mount)
 * <SessionComposer
 *   onSubmit={handleCreate}
 *   org={org}
 *   agentRef={agentRef}
 *   onAgentRefChange={setAgentRef}
 *   onAgentResolutionChange={setResolution}
 *   initialAgentRef={{ org: "stigmer", slug: "agent-creator" }}
 *   placeholder="Describe the agent you want to create..."
 *   initialRows={3}
 *   autoFocus
 * />
 *
 * // Follow-up (compact, workspace only)
 * <SessionComposer
 *   onSubmit={(msg, model) => conv.sendFollowUp(msg, { modelName: model })}
 *   disabled={!conv.canSendFollowUp}
 *   isSubmitting={conv.isSending}
 *   workspace={workspace}
 * />
 * ```
 */
export const SessionComposer = memo(function SessionComposer({ onSubmit, isSubmitting = false, disabled = false, harness, onHarnessChange, showHarnessSelector = false, defaultModelId, onModelChange, showModelSelector = true, workspace, gitHubConnection, enableGitHub = true, enableLocal = false, onBrowseLocalFolder, org, agentRef, onAgentRefChange, onAgentResolutionChange, initialAgentRef, mcpServerUsages, onMcpServerUsagesChange, skillRefs, onSkillRefsChange, runnerId, onRunnerIdChange, sessionVariables, enableAttachments = true, onAttachmentValidationError, initialAttachments, placeholder = "Reply\u2026", initialRows = 1, autoFocus = false, ariaLabel = "Send message", className, }) {
    useRenderTracer("SessionComposer", { disabled, isSubmitting });
    const [modelId, setModelIdRaw] = useState(defaultModelId);
    const userOverrodeModel = useRef(false);
    // Sync internal modelId when the external defaultModelId prop changes
    // (e.g., lastExecModelId resolves after executions load). Only sync if the
    // user hasn't made a local selection in this composer instance.
    useEffect(() => {
        if (!userOverrodeModel.current && defaultModelId !== undefined) {
            setModelIdRaw(defaultModelId);
        }
    }, [defaultModelId]);
    const setModelId = useCallback((id) => {
        userOverrodeModel.current = true;
        setModelIdRaw(id);
    }, []);
    const [displayNames, setDisplayNames] = useState(() => new Map());
    const isDisabled = disabled || isSubmitting;
    const showAgent = onAgentRefChange != null && org != null;
    const showMcp = onMcpServerUsagesChange != null && org != null;
    const showWorkspace = workspace != null;
    const showSkills = onSkillRefsChange != null && org != null;
    const showRunner = onRunnerIdChange != null && org != null;
    const showSessionVars = sessionVariables != null;
    const showAttach = enableAttachments;
    // ---------------------------------------------------------------------------
    // Runner resolution — name for contextual hint + ID for file browsing
    // ---------------------------------------------------------------------------
    const needsRunnerList = showRunner && (runnerId || (showWorkspace && enableLocal));
    const { runners: runnerListForBrowse } = useRunnerList(needsRunnerList ? (org ?? null) : null);
    const selectedRunner = useMemo(() => {
        if (!runnerId)
            return undefined;
        return runnerListForBrowse.find((r) => r.metadata?.id === runnerId);
    }, [runnerId, runnerListForBrowse]);
    const selectedRunnerName = selectedRunner?.metadata?.name;
    const selectedRunnerHostname = selectedRunner?.status?.connectionInfo?.hostname;
    const browseRunnerId = runnerId ?? null;
    // ---------------------------------------------------------------------------
    // Runner-switch safety — clear local workspace entries when runner changes
    // ---------------------------------------------------------------------------
    const prevRunnerIdRef = useRef(runnerId);
    useEffect(() => {
        if (prevRunnerIdRef.current !== runnerId) {
            const hadLocal = workspace?.entries.some((e) => e.type === "local");
            prevRunnerIdRef.current = runnerId;
            if (hadLocal) {
                workspace?.clearLocal();
            }
        }
    }, [runnerId, workspace]);
    // ---------------------------------------------------------------------------
    // Configure menu state — drives the Tier 2 drill-down popover
    // ---------------------------------------------------------------------------
    const [configOpen, setConfigOpen] = useState(false);
    const [configActivePanel, setConfigActivePanel] = useState(null);
    const configMcpInitialServerKeyRef = useRef(undefined);
    // ---------------------------------------------------------------------------
    // Session env pool — cross-references secrets across all sources
    // ---------------------------------------------------------------------------
    const personalEnv = usePersonalEnvironment((showAgent || showMcp) ? (org ?? null) : null);
    const personalEnvKeys = useMemo(() => new Set(Object.keys(personalEnv.environment?.spec?.data ?? {})), [personalEnv.environment]);
    const stigmer = useStigmer();
    const pool = useSessionEnvPool({
        personalEnvKeys,
        manualSecrets: sessionVariables?.entries,
    });
    const poolKeysWithSystem = useMemo(() => new Set([...pool.availableKeys, ...SYSTEM_ENV_VAR_KEYS]), [pool.availableKeys]);
    // ---------------------------------------------------------------------------
    // Setup hooks — instantiated before handleSubmit so it can read their state
    // ---------------------------------------------------------------------------
    const agentSetup = useAgentSetup(showAgent ? (org ?? null) : null, poolKeysWithSystem);
    const mcpSetup = useMcpServerSetup(showMcp ? (org ?? null) : null, poolKeysWithSystem);
    // ---------------------------------------------------------------------------
    // Pool-resolve notification — when POOL_RESOLVE transitions agentSetup to
    // "ready" reactively (via the useEffect inside useAgentSetup), the
    // imperative callbacks (handleAgentSelect, handleEnvSubmit) are not
    // involved, so onAgentResolutionChange would never fire. This effect
    // bridges that gap so the parent always knows the current resolution.
    // ---------------------------------------------------------------------------
    useEffect(() => {
        if (agentSetup.state.status !== "ready")
            return;
        onAgentResolutionChange?.(agentSetup.state.resolution);
    }, [agentSetup.state, onAgentResolutionChange]);
    // ---------------------------------------------------------------------------
    // Attachments — file upload state machine
    // ---------------------------------------------------------------------------
    const attachments = useAttachments(enableAttachments
        ? { onValidationError: onAttachmentValidationError }
        : undefined);
    const fileInputRef = useRef(null);
    const [isDragOver, setIsDragOver] = useState(false);
    const handleFileInputChange = useCallback((e) => {
        if (e.target.files && e.target.files.length > 0) {
            attachments.addFiles(e.target.files);
        }
        if (fileInputRef.current)
            fileInputRef.current.value = "";
    }, [attachments]);
    const handleDragOver = useCallback((e) => {
        if (!enableAttachments || isDisabled)
            return;
        e.preventDefault();
        e.stopPropagation();
        setIsDragOver(true);
    }, [enableAttachments, isDisabled]);
    const handleDragLeave = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        setIsDragOver(false);
    }, []);
    const handleDrop = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        setIsDragOver(false);
        if (!enableAttachments || isDisabled)
            return;
        if (e.dataTransfer.files.length > 0) {
            attachments.addFiles(e.dataTransfer.files);
        }
    }, [enableAttachments, isDisabled, attachments]);
    // ---------------------------------------------------------------------------
    // Submit — aggregates one-time runtimeEnv from all setup flows
    // ---------------------------------------------------------------------------
    const handleSubmit = useCallback(async (message) => {
        // Persist save-for-future manual secrets before building runtimeEnv
        if (sessionVariables?.hasSaveForFutureEntries) {
            const saveVars = sessionVariables.toSaveForFutureEnv();
            if (Object.keys(saveVars).length > 0) {
                try {
                    await personalEnv.getOrCreate();
                    await personalEnv.addVariables(saveVars);
                }
                catch {
                    // Best-effort: if persistence fails, the values still flow
                    // into runtimeEnv for this execution via the one-time path.
                }
            }
        }
        const env = {};
        // System env vars first (lowest priority) — auto-resolved from
        // the Stigmer client's connection context so MCP servers and
        // agents can reach the backend without manual user input.
        const systemVars = await resolveSystemEnvVarValues(stigmer);
        Object.assign(env, systemVars);
        if (agentSetup.state.status === "ready" &&
            agentSetup.state.resolution.mode === "oneTime") {
            Object.assign(env, agentSetup.state.resolution.runtimeEnv);
        }
        const mcpEnv = mcpSetup.pendingRuntimeEnv;
        if (Object.keys(mcpEnv).length > 0) {
            Object.assign(env, mcpEnv);
        }
        if (sessionVariables && sessionVariables.hasValidEntries) {
            Object.assign(env, sessionVariables.toRuntimeEnv());
        }
        const attachmentInputs = enableAttachments
            ? attachments.toAttachmentInputs()
            : undefined;
        const hasEnv = Object.keys(env).length > 0;
        const hasAttachments = attachmentInputs !== undefined && attachmentInputs.length > 0;
        const context = hasEnv || hasAttachments
            ? {
                runtimeEnv: hasEnv ? env : undefined,
                attachments: hasAttachments ? attachmentInputs : undefined,
            }
            : undefined;
        const resolvedModelId = modelId
            ? (parseModelKey(modelId)?.modelId ?? modelId)
            : undefined;
        onSubmit(message, resolvedModelId, context);
        if (enableAttachments) {
            attachments.clear();
        }
    }, [onSubmit, modelId, stigmer, agentSetup.state, mcpSetup.pendingRuntimeEnv, sessionVariables, enableAttachments, attachments, personalEnv]);
    const composer = useComposer({
        onSubmit: handleSubmit,
        disabled: isDisabled,
    });
    const handleModelChange = useCallback((id) => {
        setModelId(id);
        onModelChange?.(id);
    }, [onModelChange]);
    const handleHarnessChange = useCallback((h) => {
        onHarnessChange?.(h);
    }, [onHarnessChange]);
    const handleDisplayNameResolved = useCallback((key, name) => {
        setDisplayNames((prev) => {
            const next = new Map(prev);
            next.set(key, name);
            return next;
        });
    }, []);
    // ---------------------------------------------------------------------------
    // Agent setup: state-machine-driven popover + environment resolution
    // ---------------------------------------------------------------------------
    const showEnvForm = agentSetup.state.status === "needsEnvVars";
    const isAgentBusy = agentSetup.state.status === "resolving" ||
        agentSetup.state.status === "submitting";
    const handleConfigOpenChange = useCallback((open) => {
        setConfigOpen(open);
        if (!open) {
            configMcpInitialServerKeyRef.current = undefined;
            if (configActivePanel === "agent" &&
                agentSetup.state.status !== "needsEnvVars" &&
                agentSetup.state.status !== "submitting") {
                agentSetup.reset();
            }
        }
    }, [configActivePanel, agentSetup]);
    const handleConfigActivePanelChange = useCallback((panel) => {
        if (configActivePanel === "agent" &&
            panel !== "agent" &&
            agentSetup.state.status !== "needsEnvVars" &&
            agentSetup.state.status !== "submitting") {
            agentSetup.reset();
        }
        setConfigActivePanel(panel);
    }, [configActivePanel, agentSetup]);
    const handleAgentSelect = useCallback(async (ref) => {
        if (!ref) {
            onAgentRefChange?.(null);
            onAgentResolutionChange?.(null);
            return;
        }
        try {
            const result = await agentSetup.resolveAgent(ref);
            if (result.status === "ready") {
                onAgentRefChange?.(result.agentRef);
                onAgentResolutionChange?.(result.resolution);
                handleDisplayNameResolved(`${result.agentRef.org}/${result.agentRef.slug}`, result.agentName);
                setConfigOpen(false);
            }
            // "needsEnvVars" — state machine transitions automatically,
            // panel content switches to env form via `showEnvForm`.
        }
        catch {
            // Error is captured by agentSetup.state.error — displayed inline.
        }
    }, [
        agentSetup,
        onAgentRefChange,
        onAgentResolutionChange,
        handleDisplayNameResolved,
    ]);
    const handleEnvFormSubmit = useCallback(async (values, { saveForFuture }) => {
        try {
            const result = await agentSetup.submitEnvVars(values, { saveForFuture });
            onAgentRefChange?.(result.agentRef);
            onAgentResolutionChange?.(result.resolution);
            handleDisplayNameResolved(`${result.agentRef.org}/${result.agentRef.slug}`, result.agentName);
            setConfigOpen(false);
        }
        catch {
            // Error is captured by agentSetup.state.error — displayed inline.
        }
    }, [
        agentSetup,
        onAgentRefChange,
        onAgentResolutionChange,
        handleDisplayNameResolved,
    ]);
    const handleAgentChipRemove = useCallback(() => {
        onAgentRefChange?.(null);
        onAgentResolutionChange?.(null);
    }, [onAgentRefChange, onAgentResolutionChange]);
    const handlePendingAgentChipRemove = useCallback(() => {
        agentSetup.reset();
        onAgentRefChange?.(null);
        onAgentResolutionChange?.(null);
    }, [agentSetup, onAgentRefChange, onAgentResolutionChange]);
    // ---------------------------------------------------------------------------
    // Initial agent: auto-resolve on mount when initialAgentRef is provided
    // ---------------------------------------------------------------------------
    const handleAgentSelectRef = useRef(handleAgentSelect);
    handleAgentSelectRef.current = handleAgentSelect;
    const initialAgentHandled = useRef(false);
    useEffect(() => {
        if (!initialAgentRef || !showAgent || !org || initialAgentHandled.current) {
            return;
        }
        let cancelled = false;
        initialAgentHandled.current = true;
        handleAgentSelectRef.current(initialAgentRef).catch(() => {
            if (!cancelled) {
                initialAgentHandled.current = false;
            }
        });
        return () => {
            cancelled = true;
        };
    }, [initialAgentRef, showAgent, org]);
    // ---------------------------------------------------------------------------
    // Initial agent: auto-open Configure > Agent when env vars are needed
    // ---------------------------------------------------------------------------
    const initialAgentConfigAutoOpened = useRef(false);
    useEffect(() => {
        if (initialAgentConfigAutoOpened.current)
            return;
        if (!initialAgentRef)
            return;
        if (agentSetup.state.status !== "needsEnvVars")
            return;
        initialAgentConfigAutoOpened.current = true;
        setConfigOpen(true);
        setConfigActivePanel("agent");
    }, [initialAgentRef, agentSetup.state.status]);
    // ---------------------------------------------------------------------------
    // Initial attachments: upload files on mount when provided
    // ---------------------------------------------------------------------------
    const initialAttachmentsHandled = useRef(false);
    useEffect(() => {
        if (initialAttachments &&
            initialAttachments.length > 0 &&
            enableAttachments &&
            !initialAttachmentsHandled.current) {
            initialAttachmentsHandled.current = true;
            attachments.addFiles(initialAttachments);
        }
    }, [initialAttachments, enableAttachments, attachments]);
    // ---------------------------------------------------------------------------
    // MCP server setup: sync usageInputs to consumer
    // ---------------------------------------------------------------------------
    useEffect(() => {
        if (!showMcp)
            return;
        onMcpServerUsagesChange?.(mcpSetup.usageInputs);
    }, [showMcp, mcpSetup.usageInputs, onMcpServerUsagesChange]);
    // ---------------------------------------------------------------------------
    // Submission blocking: MCP servers must be fully configured before send
    // ---------------------------------------------------------------------------
    const mcpBlocked = showMcp && !mcpSetup.allReady;
    const canSend = composer.canSubmit && !mcpBlocked;
    const handleTextareaKeyDown = useCallback((e) => {
        if (!canSend && e.key === "Enter" && !e.shiftKey) {
            e.preventDefault();
            return;
        }
        composer.textareaProps.onKeyDown(e);
    }, [canSend, composer.textareaProps]);
    // ---------------------------------------------------------------------------
    // Chips — aggregated from all context sources
    // ---------------------------------------------------------------------------
    const chips = useMemo(() => {
        const items = [];
        if (agentRef) {
            const refStr = `${agentRef.org}/${agentRef.slug}`;
            items.push({
                key: `agent:${refStr}`,
                label: displayNames.get(refStr) ?? agentRef.slug,
                type: "agent",
                onRemove: handleAgentChipRemove,
            });
        }
        else if (agentSetup.state.status === "needsEnvVars" ||
            agentSetup.state.status === "resolving" ||
            agentSetup.state.status === "submitting") {
            const st = agentSetup.state;
            const ref = st.agentRef;
            const refStr = `${ref.org}/${ref.slug}`;
            const name = st.status !== "resolving"
                ? st.agentName
                : (displayNames.get(refStr) ?? ref.slug);
            items.push({
                key: `agent:${refStr}`,
                label: name,
                type: "agent",
                onRemove: handlePendingAgentChipRemove,
                status: st.status === "resolving"
                    ? "loading"
                    : st.status === "submitting"
                        ? "submitting"
                        : "needsSetup",
                onClick: st.status === "needsEnvVars"
                    ? () => {
                        setConfigOpen(true);
                        setConfigActivePanel("agent");
                    }
                    : undefined,
            });
        }
        if (showMcp) {
            for (const [key, entry] of Object.entries(mcpSetup.entries)) {
                const slug = key.slice(key.indexOf("/") + 1);
                const name = entry.status !== "loading"
                    ? (entry.mcpServer.metadata?.name ?? displayNames.get(key) ?? slug)
                    : (displayNames.get(key) ?? slug);
                let detail;
                if (entry.status === "ready" &&
                    entry.discoveredTools.length > 0 &&
                    entry.enabledTools.length < entry.discoveredTools.length) {
                    detail = `${entry.enabledTools.length}/${entry.discoveredTools.length}`;
                }
                items.push({
                    key: `mcp:${key}`,
                    label: name,
                    type: "mcp",
                    onRemove: () => mcpSetup.removeServer(mcpRefFromKey(key)),
                    status: entry.status,
                    detail,
                    onClick: entry.status === "needsSetup"
                        ? () => {
                            configMcpInitialServerKeyRef.current = key;
                            setConfigOpen(true);
                            setConfigActivePanel("mcp");
                        }
                        : undefined,
                });
            }
        }
        if (skillRefs) {
            for (const ref of skillRefs) {
                const refStr = `${ref.org}/${ref.slug}`;
                items.push({
                    key: `skill:${refStr}`,
                    label: displayNames.get(refStr) ?? ref.slug,
                    type: "skill",
                    onRemove: () => {
                        onSkillRefsChange?.(skillRefs.filter((r) => `${r.org}/${r.slug}` !== refStr));
                    },
                });
            }
        }
        if (sessionVariables) {
            for (const entry of sessionVariables.entries) {
                const k = entry.key.trim();
                if (k === "")
                    continue;
                items.push({
                    key: `secret:${entry.id}`,
                    label: k,
                    type: "secret",
                    onRemove: () => sessionVariables.removeEntry(entry.id),
                });
            }
        }
        return items;
    }, [
        agentRef,
        agentSetup.state,
        handleAgentChipRemove,
        handlePendingAgentChipRemove,
        workspace,
        showMcp,
        mcpSetup.entries,
        mcpSetup.removeServer,
        skillRefs,
        showRunner,
        runnerId,
        selectedRunnerName,
        onRunnerIdChange,
        sessionVariables,
        displayNames,
        onSkillRefsChange,
    ]);
    const workspaceCount = workspace?.entries.length ?? 0;
    const mcpCount = showMcp ? Object.keys(mcpSetup.entries).length : 0;
    const skillCount = skillRefs?.length ?? 0;
    const sessionVarCount = sessionVariables?.entries.length ?? 0;
    // ---------------------------------------------------------------------------
    // Required-by map — tracks which keys are needed by which resources
    // ---------------------------------------------------------------------------
    const requiredByMap = useMemo(() => {
        const map = {};
        if (agentSetup.state.status === "needsEnvVars") {
            const agentName = agentSetup.state.agentName;
            for (const v of agentSetup.state.missingVariables) {
                (map[v.key] ??= []).push(agentName);
            }
        }
        if (showMcp) {
            for (const entry of Object.values(mcpSetup.entries)) {
                if (entry.status !== "needsSetup")
                    continue;
                const serverName = entry.mcpServer.metadata?.name ?? "MCP Server";
                for (const v of entry.missingVariables) {
                    (map[v.key] ??= []).push(serverName);
                }
            }
        }
        return map;
    }, [agentSetup.state, showMcp, mcpSetup.entries]);
    // ---------------------------------------------------------------------------
    // Configure menu — Tier 2 items and panel renderer
    // ---------------------------------------------------------------------------
    const configureItems = useMemo(() => {
        const items = [];
        if (showAgent) {
            const agentPending = !agentRef &&
                (agentSetup.state.status === "needsEnvVars" ||
                    agentSetup.state.status === "resolving" ||
                    agentSetup.state.status === "submitting");
            items.push({
                id: "agent",
                icon: _jsx(AgentIcon, {}),
                label: "Agent",
                count: agentRef || agentPending ? 1 : 0,
                hasWarning: agentPending && agentSetup.state.status === "needsEnvVars",
            });
        }
        if (showMcp) {
            items.push({
                id: "mcp",
                icon: _jsx(McpServerIcon, {}),
                label: "MCP Servers",
                count: mcpCount,
                hasWarning: mcpSetup.needsSetupCount > 0,
            });
        }
        if (showSkills) {
            items.push({
                id: "skills",
                icon: _jsx(SkillIcon, {}),
                label: "Skills",
                count: skillCount,
            });
        }
        if (showRunner && !showWorkspace) {
            items.push({
                id: "runner",
                icon: _jsx(RunnerIcon, {}),
                label: "Runner",
                count: runnerId ? 1 : 0,
            });
        }
        if (showSessionVars) {
            items.push({
                id: "sessionVars",
                icon: _jsx(SecretsIcon, {}),
                label: "Session Variables",
                count: sessionVarCount,
            });
        }
        return items;
    }, [showAgent, agentRef, agentSetup.state, showMcp, mcpCount, mcpSetup.needsSetupCount, showSkills, skillCount, showRunner, showWorkspace, runnerId, showSessionVars, sessionVarCount]);
    const renderConfigPanel = useCallback((panelId) => {
        switch (panelId) {
            case "agent":
                return showEnvForm ? (_jsxs("div", { children: [_jsx(AgentEnvForm, { agentName: agentSetup.state.status === "needsEnvVars"
                                ? agentSetup.state.agentName
                                : "Agent", variables: agentSetup.state.status === "needsEnvVars"
                                ? agentSetup.state.missingVariables
                                : [], onSubmit: handleEnvFormSubmit, onCancel: () => agentSetup.reset(), isSubmitting: isAgentBusy, disabled: isDisabled, poolValues: pool.getAvailableValue }), agentSetup.state.error && (_jsx(AgentSetupError, { error: agentSetup.state.error }))] })) : (_jsxs("div", { className: "relative", children: [_jsx(AgentPicker, { org: org, scope: "all", value: agentRef ?? null, onChange: handleAgentSelect, onDisplayNameResolved: handleDisplayNameResolved, disabled: isDisabled || isAgentBusy }), isAgentBusy && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center rounded-lg bg-popover/80", children: _jsx(ResolveSpinner, {}) })), agentSetup.state.error && (_jsx(AgentSetupError, { error: agentSetup.state.error }))] }));
            case "mcp":
                return (_jsx(McpServerPicker, { org: org, scope: "all", setup: {
                        entries: mcpSetup.entries,
                        onServerAdded: (ref) => mcpSetup.addServer(ref),
                        onServerRemoved: (ref) => mcpSetup.removeServer(ref),
                        onSubmitEnvVars: (ref, values, opts) => mcpSetup.submitEnvVars(ref, values, {
                            saveForFuture: opts.saveForFuture,
                        }),
                        onEnabledToolsChange: (ref, tools) => mcpSetup.setEnabledTools(ref, tools),
                    }, initialServerKey: configMcpInitialServerKeyRef.current, onDisplayNameResolved: handleDisplayNameResolved, disabled: isDisabled, poolValues: pool.getAvailableValue }));
            case "skills":
                return (_jsx(SkillPicker, { org: org, scope: "all", value: skillRefs ?? [], onChange: onSkillRefsChange, onDisplayNameResolved: handleDisplayNameResolved, disabled: isDisabled }));
            case "runner":
                return (_jsx(RunnerConfigPanel, { org: org, value: runnerId ?? null, onChange: (id) => onRunnerIdChange?.(id), disabled: isDisabled }));
            case "sessionVars":
                return (_jsx(SessionVariablesInput, { sessionVariables: sessionVariables, disabled: isDisabled, requiredByMap: requiredByMap }));
            default:
                return null;
        }
    }, [
        showEnvForm,
        agentSetup,
        isAgentBusy,
        isDisabled,
        org,
        agentRef,
        handleAgentSelect,
        handleEnvFormSubmit,
        handleDisplayNameResolved,
        mcpSetup,
        skillRefs,
        onSkillRefsChange,
        runnerId,
        onRunnerIdChange,
        sessionVariables,
        pool,
        requiredByMap,
    ]);
    // ---------------------------------------------------------------------------
    // Render
    // ---------------------------------------------------------------------------
    return (_jsx("div", { role: "form", "aria-label": ariaLabel, className: cn("shrink-0", className), children: _jsxs("div", { className: cn("rounded-xl border border-border bg-card shadow-sm", "focus-within:ring-2 focus-within:ring-ring", isDisabled && "opacity-50", isDragOver && "ring-2 ring-primary/50"), onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [_jsxs("div", { className: "relative", children: [_jsx("textarea", { ...composer.textareaProps, onKeyDown: handleTextareaKeyDown, placeholder: placeholder, rows: initialRows, autoFocus: autoFocus, className: "block w-full resize-none bg-transparent px-4 pt-3 pb-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed" }), isDragOver && (_jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center rounded-t-xl bg-primary-subtle", children: _jsx("span", { className: "text-xs font-medium text-primary", children: "Drop files to attach" }) }))] }), showAttach && (_jsx("input", { ref: fileInputRef, type: "file", multiple: true, onChange: handleFileInputChange, className: "hidden", "aria-hidden": "true", tabIndex: -1 })), chips.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1.5 px-3 pb-2", children: chips.map((chip) => (_jsx(ContextChip, { label: chip.label, type: chip.type, onRemove: chip.onRemove, disabled: isDisabled, status: chip.status, detail: chip.detail, onClick: chip.onClick }, chip.key))) })), showAttach && attachments.hasEntries && (_jsx(AttachmentChipList, { entries: attachments.entries, onRemove: attachments.removeEntry, onRetry: attachments.retryEntry, disabled: isDisabled, className: "px-3 pb-2" })), showAgent &&
                    agentSetup.state.status === "needsEnvVars" &&
                    !agentRef && (_jsxs("div", { role: "status", className: "mx-3 mb-2 flex items-center gap-2 rounded-md bg-warning/10 px-2.5 py-1.5 text-xs text-warning", children: [_jsx(AlertTriangleIcon, {}), _jsx("span", { children: "Agent needs configuration before use" }), _jsx("button", { type: "button", onClick: () => {
                                setConfigOpen(true);
                                setConfigActivePanel("agent");
                            }, disabled: isDisabled, className: "ml-auto shrink-0 rounded px-1.5 py-0.5 text-[0.6rem] font-medium hover:bg-warning/20 disabled:pointer-events-none disabled:opacity-50", children: "Configure" })] })), showMcp && mcpSetup.needsSetupCount > 0 && (_jsxs("div", { role: "status", className: "mx-3 mb-2 flex items-center gap-2 rounded-md bg-warning/10 px-2.5 py-1.5 text-xs text-warning", children: [_jsx(AlertTriangleIcon, {}), _jsx("span", { children: mcpSetup.needsSetupCount === 1
                                ? "1 MCP server needs configuration"
                                : `${mcpSetup.needsSetupCount} MCP servers need configuration` }), _jsx("button", { type: "button", onClick: () => {
                                if (mcpSetup.needsSetupCount === 1) {
                                    const key = Object.entries(mcpSetup.entries).find(([, e]) => e.status === "needsSetup")?.[0];
                                    configMcpInitialServerKeyRef.current = key;
                                }
                                else {
                                    configMcpInitialServerKeyRef.current = undefined;
                                }
                                setConfigOpen(true);
                                setConfigActivePanel("mcp");
                            }, disabled: isDisabled, className: "ml-auto shrink-0 rounded px-1.5 py-0.5 text-[0.6rem] font-medium hover:bg-warning/20 disabled:pointer-events-none disabled:opacity-50", children: "Configure" })] })), _jsx(ComposerToolbar, { disabled: isDisabled, isSubmitting: isSubmitting, canSend: canSend, onSend: composer.submit, showAttach: showAttach, attachmentCount: attachments.entries.length, onAttachClick: () => fileInputRef.current?.click(), showWorkspace: showWorkspace, workspaceCount: workspaceCount, workspaceContent: workspace
                        ? _jsxs("div", { className: "space-y-3", children: [showRunner && org && (_jsx(WorkspaceRunnerSelector, { org: org, value: runnerId ?? null, onChange: (id) => onRunnerIdChange?.(id), disabled: isDisabled })), _jsx(WorkspaceEditor, { workspace: workspace, disabled: isDisabled, gitHubConnection: gitHubConnection, enableGitHub: enableGitHub, enableLocal: enableLocal, runnerId: browseRunnerId, onBrowseLocalFolder: runnerId ? onBrowseLocalFolder : undefined, runnerName: selectedRunnerName, runnerHostname: selectedRunnerHostname })] })
                        : null, configureItems: configureItems, configOpen: configOpen, onConfigOpenChange: handleConfigOpenChange, configActivePanel: configActivePanel, onConfigActivePanelChange: handleConfigActivePanelChange, renderConfigPanel: renderConfigPanel, showHarnessSelector: showHarnessSelector, harness: harness, onHarnessChange: handleHarnessChange, showModelSelector: showModelSelector, modelId: modelId, onModelChange: handleModelChange })] }) }));
});
// ---------------------------------------------------------------------------
// Agent setup error — secret-flow guidance or generic fallback
// ---------------------------------------------------------------------------
function AgentSetupError({ error }) {
    if (isSecretFlowError(error)) {
        return _jsx(SecretFlowErrorGuide, { error: error, className: "mt-2" });
    }
    return (_jsx("p", { className: "mt-2 text-xs text-destructive", children: getUserMessage(error) }));
}
// ---------------------------------------------------------------------------
// MCP key-to-ref utility
// ---------------------------------------------------------------------------
function mcpRefFromKey(key) {
    const idx = key.indexOf("/");
    return {
        org: key.slice(0, idx),
        slug: key.slice(idx + 1),
        kind: ApiResourceKind.mcp_server,
    };
}
// ---------------------------------------------------------------------------
// Runner config panel — inline list for the Configure menu
// ---------------------------------------------------------------------------
function RunnerConfigPanel({ org, value, onChange, disabled, }) {
    const { runners, isLoading } = useRunnerList(org);
    const { active, inactive } = useMemo(() => {
        const act = [];
        const inact = [];
        for (const r of runners) {
            if (isActivePhase(r.status?.phase ?? RunnerPhase.UNSPECIFIED)) {
                act.push(r);
            }
            else {
                inact.push(r);
            }
        }
        const sorter = (a, b) => {
            const pa = a.status?.phase ?? RunnerPhase.UNSPECIFIED;
            const pb = b.status?.phase ?? RunnerPhase.UNSPECIFIED;
            const po = PHASE_SORT_ORDER[pa] - PHASE_SORT_ORDER[pb];
            if (po !== 0)
                return po;
            return (a.metadata?.name ?? "").localeCompare(b.metadata?.name ?? "");
        };
        act.sort(sorter);
        inact.sort(sorter);
        return { active: act, inactive: inact };
    }, [runners]);
    const isAutoSelected = value === null;
    return (_jsxs("div", { className: "w-72 space-y-1", children: [_jsxs("button", { type: "button", onClick: () => onChange(null), disabled: disabled, className: cn("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs transition-colors", "disabled:pointer-events-none disabled:opacity-50", isAutoSelected
                    ? "bg-accent font-medium text-foreground"
                    : "text-foreground hover:bg-accent-hover"), role: "option", "aria-selected": isAutoSelected, children: [_jsx("span", { className: "inline-block h-1.5 w-1.5 shrink-0 rounded-full bg-primary", "aria-hidden": "true" }), _jsx("span", { className: "flex-1", children: "Default" }), _jsx("span", { className: "text-[0.6rem] text-muted-foreground", children: "auto" })] }), active.length > 0 && (_jsxs(_Fragment, { children: [_jsx("div", { className: "px-2 pt-1 text-[0.65rem] font-medium uppercase tracking-wider text-muted-foreground", children: "Available" }), active.map((r) => {
                        const id = r.metadata.id;
                        const name = r.metadata?.name ?? "Unnamed";
                        const phase = r.status?.phase ?? RunnerPhase.UNSPECIFIED;
                        const hostname = r.status?.connectionInfo?.hostname;
                        const isSelected = value === id;
                        return (_jsxs("button", { type: "button", onClick: () => onChange(id), disabled: disabled, className: cn("flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs transition-colors", "disabled:pointer-events-none disabled:opacity-50", isSelected
                                ? "bg-accent font-medium text-foreground"
                                : "text-foreground hover:bg-accent-hover"), role: "option", "aria-selected": isSelected, children: [_jsx("span", { className: `inline-block h-1.5 w-1.5 shrink-0 rounded-full ${phaseDotColor(phase)}`, "aria-hidden": "true" }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "truncate", children: name }), hostname && (_jsx("span", { className: "truncate text-[0.6rem] leading-tight text-muted-foreground", children: hostname }))] }), _jsx("span", { className: "shrink-0 text-[0.6rem] lowercase text-muted-foreground", children: phaseLabel(phase) })] }, id));
                    })] })), inactive.length > 0 && (_jsxs(_Fragment, { children: [_jsx("div", { className: "px-2 pt-1 text-[0.65rem] font-medium uppercase tracking-wider text-muted-foreground", children: "Offline" }), inactive.map((r) => {
                        const id = r.metadata.id;
                        const name = r.metadata?.name ?? "Unnamed";
                        const phase = r.status?.phase ?? RunnerPhase.UNSPECIFIED;
                        return (_jsxs("div", { className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs text-muted-foreground opacity-50", children: [_jsx("span", { className: `inline-block h-1.5 w-1.5 shrink-0 rounded-full ${phaseDotColor(phase)}`, "aria-hidden": "true" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: name }), _jsx("span", { className: "shrink-0 text-[0.6rem] lowercase", children: phaseLabel(phase) })] }, id));
                    })] })), runners.length === 0 && !isLoading && (_jsx("p", { className: "py-3 text-center text-xs text-muted-foreground", children: "No runners registered. Use the CLI or desktop app to start one." })), isLoading && (_jsx("div", { className: "py-3 text-center text-xs text-muted-foreground", children: "Loading runners..." })), _jsx("p", { className: "px-2 pt-1 text-[0.6rem] leading-relaxed text-muted-foreground", children: "Default assigns a cloud runner automatically. Select a specific runner to execute on your own machine." })] }));
}
//# sourceMappingURL=SessionComposer.js.map