"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { lazy, memo, Suspense, useCallback, useMemo } from "react";
import { create } from "@bufbuild/protobuf";
import { AgentMessageSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
import { ExecutionPhase, MessageType, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
import { cn } from "@stigmer/theme";
import { isTerminalPhase } from "./execution-phases";
import { MessageEntry } from "./MessageEntry";
import { ToolCallGroup } from "./ToolCallGroup";
import { SubAgentSection } from "./SubAgentSection";
import { ExecutionPhaseBadge } from "./ExecutionPhaseBadge";
import { SetupProgress } from "./SetupProgress";
import { ApprovalCard } from "./ApprovalCard";
import { FilePathContext } from "./FilePathContext";
import { SandboxContext } from "./SandboxContext";
import { useRenderTracer, useKeyStability, useDomNodeCount, DevProfiler } from "../internal/dev";
import { useAutoScroll } from "../internal/useAutoScroll";
import { JumpToLatestButton } from "../internal/JumpToLatestButton";
import { ThreadItemWrapper } from "../internal/ThreadItemWrapper";
const LazyVirtualizedThread = lazy(() => import("../internal/VirtualizedThread").then((m) => ({
    default: m.VirtualizedThread,
})));
function hasAiMessages(execution) {
    const messages = execution.status?.messages;
    if (!messages || messages.length === 0)
        return false;
    return messages.some((m) => m.type === MessageType.MESSAGE_AI && (m.content.trim() || m.toolCalls.length > 0));
}
/**
 * Builds a flat list of renderable thread items from execution data.
 *
 * Keys use stable execution IDs (not array indices) so React can
 * reconcile items across renders without unnecessary remounts.
 *
 * @internal Exported for testing — not part of the public API.
 */
export function buildThreadItems(executions, activeStreamExecution, pendingUserMessage, includeApprovals, workspaceEntries) {
    const items = [];
    const allExecutions = activeStreamExecution
        ? [...executions, activeStreamExecution]
        : executions;
    const activeStreamIndex = activeStreamExecution
        ? allExecutions.length - 1
        : -1;
    for (let ei = 0; ei < allExecutions.length; ei++) {
        const exec = allExecutions[ei];
        const execId = exec.metadata?.id ?? `_e${ei}`;
        const isActiveStreamExec = ei === activeStreamIndex;
        const messages = exec.status?.messages ?? [];
        const subAgents = exec.status?.subAgentExecutions ?? [];
        const specMessage = exec.spec?.message;
        if (specMessage && specMessage !== "execute") {
            const syntheticHumanMsg = create(AgentMessageSchema);
            syntheticHumanMsg.type = MessageType.MESSAGE_HUMAN;
            syntheticHumanMsg.content = specMessage;
            // When the active stream execution's spec message matches the
            // pending user message, use a shared bridging key so React
            // updates the pending bubble in place instead of remounting.
            const bridgePending = isActiveStreamExec &&
                pendingUserMessage != null &&
                specMessage === pendingUserMessage;
            items.push({
                kind: "message",
                message: syntheticHumanMsg,
                key: bridgePending ? "pending-user-turn" : `${execId}-spec`,
            });
        }
        for (let mi = 0; mi < messages.length; mi++) {
            const msg = messages[mi];
            // MESSAGE_TOOL messages are not rendered — tool calls are attached
            // to their parent MESSAGE_AI and rendered via ToolCallGroup.
            if (msg.type === MessageType.MESSAGE_TOOL)
                continue;
            const isEmptyAi = msg.type === MessageType.MESSAGE_AI && !msg.content.trim();
            if (!isEmptyAi) {
                items.push({
                    kind: "message",
                    message: msg,
                    key: `${execId}-m${mi}`,
                });
            }
            if (msg.type === MessageType.MESSAGE_AI &&
                msg.toolCalls.length > 0) {
                const hasTaskTools = msg.toolCalls.some((tc) => tc.name === "task");
                if (hasTaskTools) {
                    const regularTools = [];
                    const matchedSubAgents = [];
                    for (const tc of msg.toolCalls) {
                        if (tc.name === "task") {
                            const matched = subAgents.find((sa) => sa.id === tc.id);
                            if (matched)
                                matchedSubAgents.push(matched);
                        }
                        else {
                            regularTools.push(tc);
                        }
                    }
                    if (regularTools.length > 0) {
                        items.push({
                            kind: "tool-group",
                            toolCalls: regularTools,
                            subAgentExecutions: subAgents,
                            key: `${execId}-m${mi}-tc`,
                        });
                    }
                    for (const sa of matchedSubAgents) {
                        items.push({
                            kind: "sub-agent",
                            subAgentExecution: sa,
                            key: `sa-${sa.id}`,
                        });
                    }
                }
                else {
                    items.push({
                        kind: "tool-group",
                        toolCalls: msg.toolCalls,
                        subAgentExecutions: subAgents,
                        key: `${execId}-m${mi}-tc`,
                    });
                }
            }
        }
    }
    const lastExec = allExecutions[allExecutions.length - 1];
    const lastPhase = lastExec?.status?.phase ?? ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
    if (activeStreamExecution &&
        (lastPhase === ExecutionPhase.EXECUTION_PENDING ||
            lastPhase === ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED) &&
        !hasAiMessages(activeStreamExecution)) {
        const serverPhase = activeStreamExecution.status?.setupProgress?.currentPhase || undefined;
        items.push({
            kind: "setup-progress",
            workspaceEntries: workspaceEntries ?? [],
            serverPhase,
            key: "setup-progress",
        });
    }
    if (isTerminalPhase(lastPhase) &&
        lastPhase !== ExecutionPhase.EXECUTION_COMPLETED) {
        items.push({
            kind: "phase-badge",
            phase: lastPhase,
            key: `phase-${lastPhase}`,
        });
    }
    if (includeApprovals) {
        const allApprovals = lastExec?.status?.pendingApprovals ?? [];
        for (let ai = 0; ai < allApprovals.length; ai++) {
            const approval = allApprovals[ai];
            items.push({
                kind: "approval-request",
                pendingApproval: approval,
                key: `approval-${approval.toolCallId || ai}`,
            });
        }
    }
    if (pendingUserMessage) {
        const alreadySynthesized = lastExec?.spec?.message === pendingUserMessage;
        if (!alreadySynthesized) {
            const syntheticPending = create(AgentMessageSchema);
            syntheticPending.type = MessageType.MESSAGE_HUMAN;
            syntheticPending.content = pendingUserMessage;
            items.push({
                kind: "message",
                message: syntheticPending,
                key: "pending-user-turn",
                isPending: true,
            });
        }
    }
    return items;
}
/**
 * Renders a continuous conversation thread from one or more
 * {@link AgentExecution} snapshots.
 *
 * Composes {@link MessageEntry}, {@link ToolCallGroup}, and
 * {@link ExecutionPhaseBadge} into a scrollable, auto-scrolling log
 * that follows new content when the user is near the bottom.
 *
 * Renders nothing when no executions are provided.
 *
 * All visual properties flow through `--stgm-*` tokens.
 *
 * @example
 * ```tsx
 * const { executions } = useSessionExecutions(sessionId);
 * const stream = useExecutionStream(activeId);
 *
 * <MessageThread
 *   executions={executions ?? []}
 *   activeStreamExecution={stream.execution}
 * />
 * ```
 */
export function MessageThread({ executions, activeStreamExecution, pendingUserMessage, className, formatToolCallSummary, onApprovalSubmit, submittingApprovalIds, workspaceEntries, onFilePathClick, sandboxWorkspaceRoot, virtualized = false, }) {
    useRenderTracer("MessageThread", { executions, activeStreamExecution });
    const includeApprovals = onApprovalSubmit != null;
    const items = useMemo(() => buildThreadItems(executions, activeStreamExecution, pendingUserMessage, includeApprovals, workspaceEntries), [executions, activeStreamExecution, pendingUserMessage, includeApprovals, workspaceEntries]);
    useKeyStability(items);
    const filePathCtx = useMemo(() => ({
        workspaceEntries: workspaceEntries ?? [],
        onFilePathClick,
    }), [workspaceEntries, onFilePathClick]);
    const sandboxCtx = useMemo(() => ({ sandboxWorkspaceRoot: sandboxWorkspaceRoot ?? "" }), [sandboxWorkspaceRoot]);
    if (virtualized) {
        return (_jsx("div", { className: cn("relative min-h-0", className), children: _jsx(Suspense, { fallback: null, children: _jsx(LazyVirtualizedThread, { items: items, formatToolCallSummary: formatToolCallSummary, onApprovalSubmit: onApprovalSubmit, submittingApprovalIds: submittingApprovalIds, filePathCtx: filePathCtx, sandboxCtx: sandboxCtx }) }) }));
    }
    return (_jsx(NonVirtualizedThread, { items: items, className: className, formatToolCallSummary: formatToolCallSummary, onApprovalSubmit: onApprovalSubmit, submittingApprovalIds: submittingApprovalIds, filePathCtx: filePathCtx, sandboxCtx: sandboxCtx }));
}
function NonVirtualizedThread({ items, className, formatToolCallSummary, onApprovalSubmit, submittingApprovalIds, filePathCtx, sandboxCtx, }) {
    const { scrollRef, sentinelRef, contentRef, isFollowing, jumpToLatest } = useAutoScroll();
    useDomNodeCount(scrollRef, "MessageThread");
    return (_jsxs("div", { className: cn("relative min-h-0", className), children: [_jsxs("div", { ref: scrollRef, role: "log", "aria-live": "polite", "aria-relevant": "additions", className: cn("h-full overflow-y-auto pt-6 pb-4 [overflow-anchor:none]", "[scrollbar-width:thin] [scrollbar-color:var(--color-border)_transparent]", "[&::-webkit-scrollbar]:w-1.5", "[&::-webkit-scrollbar-track]:bg-transparent", "[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border/40"), children: [_jsx(SandboxContext.Provider, { value: sandboxCtx, children: _jsx(FilePathContext.Provider, { value: filePathCtx, children: _jsx(DevProfiler, { id: "MessageThread", children: _jsx("div", { ref: contentRef, className: "flex flex-col gap-4", children: items.map((item) => (_jsx(ThreadItemWrapper, { animate: true, children: _jsx(ThreadItemRenderer, { item: item, formatToolCallSummary: formatToolCallSummary, onApprovalSubmit: onApprovalSubmit, submittingApprovalIds: submittingApprovalIds }) }, item.key))) }) }) }) }), _jsx("div", { ref: sentinelRef, "aria-hidden": "true" })] }), _jsx(JumpToLatestButton, { onClick: jumpToLatest, visible: !isFollowing })] }));
}
/**
 * Renders a single thread item by discriminated `kind`. Used by both
 * the non-virtualized `items.map()` path and the virtualized
 * `Virtuoso.itemContent` callback.
 *
 * Does not receive a `key` prop — the caller is responsible for
 * keying (either via `items.map` or `computeItemKey`).
 *
 * @internal Exported for internal use by `VirtualizedThread` — not
 * part of the public API.
 */
export function ThreadItemRenderer({ item, formatToolCallSummary, onApprovalSubmit, submittingApprovalIds, }) {
    switch (item.kind) {
        case "message":
            return (_jsx(MessageEntry, { message: item.message, className: item.isPending ? "opacity-70" : undefined }));
        case "tool-group":
            return (_jsx(ToolCallGroup, { toolCalls: item.toolCalls, subAgentExecutions: item.subAgentExecutions, formatSummary: formatToolCallSummary, className: "mx-4" }));
        case "sub-agent":
            return (_jsx(SubAgentSection, { subAgentExecution: item.subAgentExecution, className: "mx-4" }));
        case "phase-badge":
            return (_jsx("div", { className: "flex justify-center py-3", children: _jsx(ExecutionPhaseBadge, { phase: item.phase }) }));
        case "approval-request":
            return (_jsx(ApprovalCardRow, { pendingApproval: item.pendingApproval, onApprovalSubmit: onApprovalSubmit, isSubmitting: submittingApprovalIds?.has(item.pendingApproval.toolCallId) ?? false }));
        case "setup-progress":
            return (_jsx(SetupProgress, { workspaceEntries: item.workspaceEntries, serverPhase: item.serverPhase }));
    }
}
const ApprovalCardRow = memo(function ApprovalCardRow({ pendingApproval, onApprovalSubmit, isSubmitting, }) {
    const handleSubmit = useCallback((action, comment) => {
        onApprovalSubmit(pendingApproval.toolCallId, action, comment);
    }, [onApprovalSubmit, pendingApproval.toolCallId]);
    return (_jsx(ApprovalCard, { pendingApproval: pendingApproval, onSubmit: handleSubmit, isSubmitting: isSubmitting, className: "mx-4" }));
});
//# sourceMappingURL=MessageThread.js.map