"use client";
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { memo, useState } from "react";
import { cn } from "@stigmer/theme";
import { CollapsibleJsonBlock, formatJson, humanizeArgKey, isScalar, } from "../../execution/tool-rendering-primitives.js";
/**
 * Threshold (in characters) above which a string value is rendered
 * as a wrapped prose paragraph instead of a truncated monospace `<dd>`.
 * Tuned to catch executive summaries, error descriptions, and other
 * natural-language fields while keeping IDs, slugs, and short values
 * in monospace.
 */
const PROSE_CHAR_THRESHOLD = 120;
/**
 * Maximum nesting depth at which the viewer recurses into nested
 * objects with `<dl>` sections. Beyond this depth, values fall back
 * to syntax-highlighted JSON via {@link CollapsibleJsonBlock} —
 * unless the nested value contains only scalar leaves, in which case
 * the structured view is used regardless of depth (see
 * {@link isAllScalarEntries}).
 */
const MAX_RECURSIVE_DEPTH = 5;
/** Patterns that indicate a string value is an identifier or URL, not prose. */
const ID_URL_PATTERN = /^(https?:\/\/|[a-z]{2,6}_[0-9a-zA-Z]{8,}$|[0-9a-f]{8}-[0-9a-f]{4}-|\/[a-z])/;
/**
 * Type-aware renderer for `JsonObject` task data.
 *
 * Follows the {@link McpArgsView} scalar/complex split pattern:
 * - Scalar values (string, number, boolean) render in a `<dl>` grid
 *   with humanized key labels
 * - Complex values (objects, arrays) render as collapsible sections
 *
 * Extensions beyond `McpArgsView`:
 * - Long strings (> {@link PROSE_CHAR_THRESHOLD}) that are not
 *   IDs/URLs render as wrapped prose paragraphs
 * - Nested objects recurse up to {@link MAX_RECURSIVE_DEPTH}, then
 *   fall back to syntax-highlighted JSON
 * - Arrays of scalars render inline; arrays of objects render as
 *   numbered collapsible items
 */
export const StructuredDataViewer = memo(function StructuredDataViewer({ data, className, }) {
    const entries = Object.entries(data);
    if (entries.length === 0) {
        return (_jsx("p", { className: cn("text-xs text-muted-foreground", className), children: "No data fields" }));
    }
    return (_jsx("div", { className: cn("space-y-3 text-xs", className), children: _jsx(ObjectEntries, { entries: entries, depth: 0 }) }));
});
// ---------------------------------------------------------------------------
// Recursive entry renderer
// ---------------------------------------------------------------------------
function ObjectEntries({ entries, depth, }) {
    const scalars = [];
    const complex = [];
    for (const [key, value] of entries) {
        if (isScalar(value) || value === null || value === undefined) {
            scalars.push([key, value]);
        }
        else {
            complex.push([key, value]);
        }
    }
    return (_jsxs(_Fragment, { children: [scalars.length > 0 && (_jsx("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 rounded-md border border-border bg-muted-faint px-2.5 py-2", children: scalars.map(([key, value]) => (_jsx(ScalarEntry, { label: key, value: value }, key))) })), complex.map(([key, value]) => (_jsx(ComplexEntry, { label: key, value: value, depth: depth }, key)))] }));
}
// ---------------------------------------------------------------------------
// Scalar entry (inline key-value row)
// ---------------------------------------------------------------------------
function ScalarEntry({ label, value, }) {
    const strValue = value === null || value === undefined ? "—" : String(value);
    const isLongProse = typeof value === "string" &&
        strValue.length > PROSE_CHAR_THRESHOLD &&
        !ID_URL_PATTERN.test(strValue);
    return (_jsxs(_Fragment, { children: [_jsx("dt", { className: "whitespace-nowrap font-mono text-muted-foreground", children: humanizeArgKey(label) }), isLongProse ? (_jsx("dd", { className: "min-w-0", children: _jsx(ProseValue, { text: strValue }) })) : (_jsx("dd", { className: cn("min-w-0 font-mono text-foreground", typeof value === "boolean" && "font-medium", (value === null || value === undefined) && "text-muted-foreground"), title: strValue, children: strValue.includes("\n") ? (_jsx("pre", { className: "whitespace-pre-wrap break-words rounded border border-border bg-muted-subtle px-2 py-1 font-mono text-foreground", children: strValue })) : (_jsx("span", { className: "block truncate", children: strValue })) }))] }));
}
// ---------------------------------------------------------------------------
// Prose value (long wrapped text with expand/collapse)
// ---------------------------------------------------------------------------
const PROSE_PREVIEW_CHARS = 300;
function ProseValue({ text }) {
    const needsTruncation = text.length > PROSE_PREVIEW_CHARS;
    const [expanded, setExpanded] = useState(false);
    const displayed = needsTruncation && !expanded
        ? text.slice(0, PROSE_PREVIEW_CHARS) + "\u2026"
        : text;
    return (_jsxs("div", { children: [_jsx("p", { className: "whitespace-pre-wrap break-words leading-relaxed text-foreground", children: displayed }), needsTruncation && (_jsx("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "mt-0.5 text-xs font-medium text-primary transition-colors hover:text-primary-muted", children: expanded ? "Show less" : "Show more" }))] }));
}
// ---------------------------------------------------------------------------
// Complex entry (nested object or array)
// ---------------------------------------------------------------------------
function ComplexEntry({ label, value, depth, }) {
    if (Array.isArray(value)) {
        return _jsx(ArrayEntry, { label: label, items: value, depth: depth });
    }
    if (typeof value === "object" && value !== null) {
        const entries = Object.entries(value);
        if (entries.length === 0) {
            return (_jsx(CollapsibleJsonBlock, { label: humanizeArgKey(label), content: formatJson(value) }));
        }
        if (depth + 1 >= MAX_RECURSIVE_DEPTH &&
            !isAllScalarEntries(entries)) {
            return (_jsx(CollapsibleJsonBlock, { label: humanizeArgKey(label), content: formatJson(value) }));
        }
        return (_jsx(NestedSection, { label: humanizeArgKey(label), children: _jsx(ObjectEntries, { entries: entries, depth: depth + 1 }) }));
    }
    return (_jsx(CollapsibleJsonBlock, { label: humanizeArgKey(label), content: formatJson(value) }));
}
// ---------------------------------------------------------------------------
// Array entry
// ---------------------------------------------------------------------------
const INLINE_SCALAR_ARRAY_LIMIT = 5;
/**
 * Keys checked (in priority order) to extract a human-readable label
 * from an object array item. The first key found with a scalar value
 * is used as the item subtitle.
 */
const ITEM_LABEL_KEYS = ["name", "title", "label", "id"];
/** Above this count, individual items start collapsed to avoid viewport flood. */
const AUTO_COLLAPSE_ITEM_THRESHOLD = 3;
function ArrayEntry({ label, items, depth, }) {
    if (items.length === 0) {
        return (_jsxs("div", { className: "flex items-baseline gap-1.5", children: [_jsx("span", { className: "font-medium text-muted-foreground", children: humanizeArgKey(label) }), _jsx("span", { className: "text-muted-foreground-subtle", children: "(0 items)" })] }));
    }
    const allScalars = items.every((item) => isScalar(item) || item === null || item === undefined);
    if (allScalars && items.length <= INLINE_SCALAR_ARRAY_LIMIT) {
        return (_jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "font-medium text-muted-foreground", children: humanizeArgKey(label) }), _jsx("p", { className: "font-mono text-foreground", children: items.map((item) => String(item ?? "null")).join(", ") })] }));
    }
    const allObjects = items.every((item) => typeof item === "object" && item !== null && !Array.isArray(item));
    const objectItems = items;
    if (allObjects &&
        (depth < MAX_RECURSIVE_DEPTH ||
            isAllScalarObjectArray(objectItems))) {
        return (_jsx(ObjectArraySection, { label: label, items: objectItems, depth: depth }));
    }
    return (_jsx(CollapsibleJsonBlock, { label: `${humanizeArgKey(label)} (${items.length} items)`, content: formatJson(items) }));
}
// ---------------------------------------------------------------------------
// Object array section (structured rendering for arrays of objects)
// ---------------------------------------------------------------------------
function ObjectArraySection({ label, items, depth, }) {
    const [expanded, setExpanded] = useState(true);
    const defaultItemExpanded = items.length <= AUTO_COLLAPSE_ITEM_THRESHOLD;
    return (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "flex items-center gap-1 font-medium text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(ChevronIcon, { expanded: expanded }), humanizeArgKey(label), " ", _jsxs("span", { className: "font-normal text-muted-foreground-subtle", children: ["(", items.length, " ", items.length === 1 ? "item" : "items", ")"] })] }), expanded && (_jsx("div", { className: "space-y-1.5 border-l-2 border-border pl-3", children: items.map((item, index) => (_jsx(ObjectArrayItem, { item: item, index: index, depth: depth, defaultExpanded: defaultItemExpanded }, index))) }))] }));
}
function ObjectArrayItem({ item, index, depth, defaultExpanded, }) {
    const [expanded, setExpanded] = useState(defaultExpanded);
    const entries = Object.entries(item);
    const subtitle = extractItemLabel(item);
    const itemLabel = subtitle
        ? `Item ${index + 1} \u2014 ${subtitle}`
        : `Item ${index + 1}`;
    if (entries.length === 0) {
        return (_jsxs("div", { className: "text-muted-foreground", children: [itemLabel, " ", _jsx("span", { className: "font-normal text-muted-foreground-subtle", children: "(empty)" })] }));
    }
    return (_jsxs("div", { className: "space-y-1", children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "flex items-center gap-1 text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(ChevronIcon, { expanded: expanded }), _jsx("span", { className: "font-medium", children: `Item ${index + 1}` }), subtitle && (_jsxs("span", { className: "font-normal text-muted-foreground-subtle", children: ["\u2014 ", subtitle] }))] }), expanded && (_jsx("div", { className: "border-l-2 border-border pl-3", children: _jsx(ObjectEntries, { entries: entries, depth: depth + 1 }) }))] }));
}
/**
 * Scans an object for a well-known label key and returns its scalar
 * value as a string, or `null` if none found.
 */
function extractItemLabel(item) {
    for (const key of ITEM_LABEL_KEYS) {
        const value = item[key];
        if (isScalar(value))
            return String(value);
    }
    return null;
}
// ---------------------------------------------------------------------------
// Scalar-leaf bypass
// ---------------------------------------------------------------------------
/**
 * Returns `true` when every value in the entry list is a scalar
 * (string, number, boolean) or nullish. Used to bypass the
 * {@link MAX_RECURSIVE_DEPTH} gate for terminal objects that contain
 * no further complex nesting — rendering them as a structured `<dl>`
 * grid is always safe and more readable than a JSON block.
 */
function isAllScalarEntries(entries) {
    return entries.every(([, value]) => isScalar(value) || value === null || value === undefined);
}
/**
 * Returns `true` when every item in an object array contains only
 * scalar (or nullish) values. When this holds, the array can be
 * rendered as structured collapsible items regardless of depth.
 */
function isAllScalarObjectArray(items) {
    return items.every((item) => isAllScalarEntries(Object.entries(item)));
}
// ---------------------------------------------------------------------------
// Nested section (collapsible group for one level of nesting)
// ---------------------------------------------------------------------------
function NestedSection({ label, children, }) {
    const [expanded, setExpanded] = useState(true);
    return (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "flex items-center gap-1 font-medium text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(ChevronIcon, { expanded: expanded }), label] }), expanded && (_jsx("div", { className: "border-l-2 border-border pl-3", children: children }))] }));
}
// ---------------------------------------------------------------------------
// Shared chevron icon for collapsible sections
// ---------------------------------------------------------------------------
function ChevronIcon({ expanded }) {
    return (_jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", className: cn("shrink-0 transition-transform duration-150", expanded && "rotate-90"), "aria-hidden": "true", children: _jsx("path", { d: "M2 1L6 4L2 7" }) }));
}
//# sourceMappingURL=StructuredDataViewer.js.map