"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
// Renders a normalized ToolResultView (from @stigmer/sdk) with a semantic view
// per result type — diffs, terminals, search lists, file content, MCP content
// blocks — instead of a raw JSON dump. The json/text variants are the graceful
// fallbacks, so unknown tools are still readable.
import { useMemo } from "react";
import { cn } from "@stigmer/theme";
import { computeDiff } from "../version-history/computeDiff";
import { CollapsibleCode, CollapsiblePre, formatJson } from "./tool-rendering-primitives";
import { FilePathLink } from "./FilePathLink";
import { useSandboxNormalize } from "./SandboxContext";
/**
 * Renders a {@link ToolResultView} with a type-appropriate treatment.
 *
 * This is the presentation half of the tool-result pipeline: `@stigmer/sdk`'s
 * `normalizeToolResult` turns an opaque result string into a typed view, and
 * this component renders it. Importable on its own by platform builders who
 * compose custom tool UIs.
 */
export function ResultView({ view, className }) {
    switch (view.type) {
        case "diff":
            return _jsx(DiffResultView, { view: view, className: className });
        case "terminal":
            return _jsx(TerminalResultView, { view: view, className: className });
        case "search":
            return _jsx(SearchResultView, { matches: view.matches, count: view.count, className: className });
        case "list":
            return _jsx(ListResultView, { entries: view.entries, count: view.count, className: className });
        case "file":
            return _jsx(FileResultView, { view: view, className: className });
        case "contentBlocks":
            return _jsx(ContentBlocksResultView, { blocks: view.blocks, className: className });
        case "text":
            return view.text ? _jsx(CollapsiblePre, { content: view.text, className: cn("text-foreground", className) }) : null;
        case "json":
            return _jsx(CollapsibleCode, { label: "Result", content: formatJson(view.value), className: className });
        case "error":
            return _jsx(ErrorResultView, { message: view.message, className: className });
        case "empty":
            return null;
    }
}
/**
 * Returns a compact one-line summary fragment for a result view, suitable for a
 * collapsed tool row (e.g. "+40 -0", "exit 1", "2 matches"). Returns null when
 * there is nothing useful to summarize.
 */
export function summarizeResultView(view) {
    switch (view.type) {
        case "diff": {
            const stats = diffStats(view);
            return stats ? `+${stats.added} -${stats.removed}` : null;
        }
        case "terminal":
            return view.exitCode !== undefined && view.exitCode !== 0 ? `exit ${view.exitCode}` : null;
        case "search":
            return `${view.count} ${view.count === 1 ? "match" : "matches"}`;
        case "list":
            return `${view.count} ${view.count === 1 ? "item" : "items"}`;
        default:
            return null;
    }
}
function countHunks(hunks) {
    let added = 0;
    let removed = 0;
    for (const hunk of hunks) {
        for (const line of hunk.lines) {
            if (line.type === "added")
                added++;
            else if (line.type === "removed")
                removed++;
        }
    }
    return { added, removed };
}
// Used by the collapsed-row summary, where no precomputed hunks are available.
// Prefers the engine-provided counts (Cursor envelope) and only runs the diff
// algorithm for the native harness, which provides none.
function diffStats(view) {
    if (view.linesAdded !== undefined || view.linesRemoved !== undefined) {
        return { added: view.linesAdded ?? 0, removed: view.linesRemoved ?? 0 };
    }
    if (view.oldText !== undefined && view.newText !== undefined) {
        return countHunks(computeDiff(view.oldText, view.newText));
    }
    return null;
}
function DiffResultView({ view, className }) {
    // The native engine returns no diff, so we compute hunks from args (old/new).
    // The Cursor envelope provides a ready unified-diff string we render directly.
    const hunks = useMemo(() => {
        if (view.oldText !== undefined && view.newText !== undefined) {
            return computeDiff(view.oldText, view.newText);
        }
        return [];
    }, [view.oldText, view.newText]);
    // Derive stats from the already-computed hunks instead of re-running the diff.
    const stats = useMemo(() => {
        if (view.linesAdded !== undefined || view.linesRemoved !== undefined) {
            return { added: view.linesAdded ?? 0, removed: view.linesRemoved ?? 0 };
        }
        return hunks.length > 0 ? countHunks(hunks) : null;
    }, [view.linesAdded, view.linesRemoved, hunks]);
    return (_jsxs("div", { className: cn("space-y-1", className), children: [_jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [view.path && _jsx(FilePathLink, { path: view.path, className: "text-xs" }), stats && (_jsxs("span", { className: "shrink-0 tabular-nums text-xs", children: [_jsxs("span", { className: "text-success", children: ["+", stats.added] }), " ", _jsxs("span", { className: "text-destructive", children: ["-", stats.removed] })] }))] }), hunks.length > 0 ? (_jsx(DiffHunks, { hunks: hunks })) : view.unifiedDiff ? (_jsx(UnifiedDiffText, { patch: view.unifiedDiff })) : null] }));
}
function DiffHunks({ hunks }) {
    return (_jsx("div", { className: "overflow-auto rounded-md border border-border bg-muted-subtle font-mono text-xs", children: hunks.map((hunk, hi) => (_jsx("div", { className: "border-b border-border-muted last:border-b-0", children: hunk.lines.map((line, li) => (_jsxs("div", { className: cn("whitespace-pre-wrap break-words px-2 py-0.5", line.type === "added" && "bg-success-subtle text-success", line.type === "removed" && "bg-destructive-subtle text-destructive", line.type === "context" && "text-muted-foreground"), children: [line.type !== "context" && (_jsx("span", { className: "sr-only", children: line.type === "added" ? "Added: " : "Removed: " })), _jsx("span", { className: "select-none", "aria-hidden": "true", children: line.type === "added" ? "+" : line.type === "removed" ? "-" : " " }), line.content] }, li))) }, hi))) }));
}
// Renders a raw unified-diff patch string (Cursor envelope) with +/- coloring.
function UnifiedDiffText({ patch }) {
    const lines = patch.split("\n");
    return (_jsx("pre", { className: "max-h-80 overflow-auto whitespace-pre-wrap break-words rounded-md border border-border bg-muted-subtle p-2 font-mono text-xs", children: lines.map((line, i) => (_jsx("div", { className: cn(line.startsWith("+") && !line.startsWith("+++") && "text-success", line.startsWith("-") && !line.startsWith("---") && "text-destructive", line.startsWith("@@") && "text-primary"), children: line || " " }, i))) }));
}
function TerminalResultView({ view, className }) {
    const normalize = useSandboxNormalize();
    const failed = view.exitCode !== undefined && view.exitCode !== 0;
    return (_jsxs("div", { className: cn("space-y-1", className), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-medium text-muted-foreground", children: "Output" }), view.exitCode !== undefined && (_jsxs("span", { className: cn("rounded px-1 py-0.5 text-[10px] font-medium leading-none tabular-nums", failed ? "bg-destructive-subtle text-destructive" : "bg-success-subtle text-success"), children: ["exit ", view.exitCode] }))] }), _jsxs("div", { className: "rounded-md border border-border bg-[var(--stgm-terminal-bg,#1a1a2e)] p-2.5", children: [view.stdout && (_jsx(CollapsiblePre, { content: normalize(view.stdout), className: "text-[var(--stgm-terminal-fg,#e0e0e0)]" })), view.stderr && (_jsx(CollapsiblePre, { content: normalize(view.stderr), className: "text-destructive" }))] })] }));
}
// ---------------------------------------------------------------------------
// Search / List
// ---------------------------------------------------------------------------
function SearchResultView({ matches, count, className, }) {
    if (count === 0) {
        return _jsx("p", { className: cn("text-xs text-muted-foreground", className), children: "No matches" });
    }
    return (_jsxs("div", { className: cn("space-y-1", className), children: [_jsxs("span", { className: "font-medium text-muted-foreground", children: [count, " ", count === 1 ? "match" : "matches"] }), _jsx("div", { className: "max-h-80 overflow-auto rounded-md border border-border bg-muted-subtle font-mono text-xs", children: matches.map((m, i) => (_jsxs("div", { className: "whitespace-pre-wrap break-words border-b border-border-muted px-2 py-0.5 text-foreground last:border-b-0", children: [m.file && _jsxs("span", { className: "text-primary", children: [m.file, m.line !== undefined ? `:${m.line}` : "", " "] }), _jsx("span", { className: m.file ? "text-muted-foreground" : "text-foreground", children: m.text })] }, i))) })] }));
}
function ListResultView({ entries, count, className, }) {
    if (count === 0) {
        return _jsx("p", { className: cn("text-xs text-muted-foreground", className), children: "Empty" });
    }
    return (_jsxs("div", { className: cn("space-y-1", className), children: [_jsxs("span", { className: "font-medium text-muted-foreground", children: [count, " ", count === 1 ? "item" : "items"] }), _jsx("div", { className: "max-h-80 overflow-auto rounded-md border border-border bg-muted-subtle font-mono text-xs", children: entries.map((e, i) => (_jsx("div", { className: "truncate px-2 py-0.5 text-foreground", children: e }, i))) })] }));
}
function FileResultView({ view, className }) {
    if (!view.content) {
        return view.path ? (_jsx("div", { className: cn("flex items-center gap-1.5 text-xs", className), children: _jsx(FilePathLink, { path: view.path, className: "text-xs" }) })) : null;
    }
    return (_jsxs("div", { className: cn("space-y-1", className), children: [view.path && _jsx(FilePathLink, { path: view.path, className: "text-xs" }), _jsx(CollapsibleCode, { label: view.truncated ? "Content (truncated)" : "Content", content: view.content })] }));
}
function ContentBlocksResultView({ blocks, className, }) {
    const text = useMemo(() => blocks
        .map((b) => (b.type === "text" && b.text ? b.text : `[${b.type}]`))
        .join("\n"), [blocks]);
    return _jsx(CollapsiblePre, { content: text, className: cn("text-foreground", className) });
}
function ErrorResultView({ message, className }) {
    return (_jsxs("div", { className: cn("space-y-1", className), children: [_jsx("span", { className: "font-medium text-destructive", children: "Error" }), _jsx("pre", { className: "whitespace-pre-wrap break-words rounded-md border border-destructive/20 bg-destructive-subtle p-2 font-mono text-destructive", children: message })] }));
}
//# sourceMappingURL=ResultView.js.map