import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/*
 * Copyright (c) 2023-2025 Datalayer, Inc.
 * Distributed under the terms of the Modified BSD License.
 */
/**
 * OtelSpanDetail – Detail panel for a selected span, with metadata,
 * collapsible attributes, gen_ai arguments, events, and links.
 *
 * Uses Primer React components for consistent theming.
 *
 * @module otel/OtelSpanDetail
 */
import { useState } from 'react';
import { Box, Text, IconButton, UnderlineNav, CounterLabel, Label, } from '@primer/react';
import { XIcon, ChevronDownIcon, ChevronRightIcon, } from '@primer/octicons-react';
import { formatDuration, buildSpanTree } from '../utils.js';
import { OtelSpanTree } from './OtelSpanTree.js';
// ── Helpers ─────────────────────────────────────────────────────────
/** Single key–value metadata row. */
const MetadataRow = ({ label, value, mono = false }) => {
    if (value === undefined || value === null || value === '')
        return null;
    return (_jsxs(Box, { sx: {
            display: 'flex',
            py: 1,
            borderBottom: '1px solid',
            borderColor: 'border.muted',
            gap: 2,
        }, children: [_jsx(Text, { sx: {
                    width: 140,
                    minWidth: 140,
                    color: 'fg.muted',
                    fontSize: 1,
                    fontWeight: 'bold',
                }, children: label }), _jsx(Text, { sx: {
                    fontSize: 1,
                    fontFamily: mono ? 'mono' : 'normal',
                    wordBreak: 'break-all',
                }, children: String(value) })] }));
};
/** Collapsible section for nested JSON/attribute data with tree rendering. */
const CollapsibleSection = ({ title, data, defaultOpen = false }) => {
    const [open, setOpen] = useState(defaultOpen);
    if (!data || Object.keys(data).length === 0)
        return null;
    return (_jsxs(Box, { sx: { mt: 3 }, children: [_jsxs(Box, { sx: {
                    display: 'flex',
                    alignItems: 'center',
                    gap: 1,
                    cursor: 'pointer',
                    py: 1,
                    userSelect: 'none',
                }, onClick: () => setOpen(!open), children: [open ? _jsx(ChevronDownIcon, { size: 16 }) : _jsx(ChevronRightIcon, { size: 16 }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'bold' }, children: title }), _jsx(CounterLabel, { children: Object.keys(data).length })] }), open && (_jsx(Box, { sx: {
                    bg: 'canvas.subtle',
                    borderRadius: 2,
                    border: '1px solid',
                    borderColor: 'border.default',
                    p: 2,
                    mt: 1,
                    overflowX: 'auto',
                }, children: Object.entries(data).map(([key, val]) => (_jsx(AttributeRow, { attrKey: key, value: val, depth: 0 }, key))) }))] }));
};
/** Recursive attribute row – supports nested objects and arrays. */
const AttributeRow = ({ attrKey, value, depth }) => {
    const [open, setOpen] = useState(depth < 1);
    const isObject = typeof value === 'object' && value !== null && !Array.isArray(value);
    const isArray = Array.isArray(value);
    const isNested = isObject || isArray;
    return (_jsxs(Box, { sx: {
            borderBottom: depth === 0 ? '1px solid' : 'none',
            borderColor: 'border.muted',
            pb: depth === 0 ? 1 : 0,
            mb: depth === 0 ? 1 : 0,
        }, children: [_jsxs(Box, { sx: {
                    display: 'flex',
                    gap: 2,
                    py: 1,
                    pl: depth * 16 + 'px',
                    alignItems: 'flex-start',
                }, children: [isNested ? (_jsx(Box, { sx: {
                            cursor: 'pointer',
                            color: 'fg.muted',
                            userSelect: 'none',
                            width: 16,
                            flexShrink: 0,
                        }, onClick: () => setOpen(!open), children: open ? (_jsx(ChevronDownIcon, { size: 12 })) : (_jsx(ChevronRightIcon, { size: 12 })) })) : (_jsx(Box, { sx: { width: 16, flexShrink: 0 } })), _jsx(Text, { sx: {
                            color: 'accent.fg',
                            fontSize: 1,
                            fontFamily: 'mono',
                            minWidth: 180,
                            wordBreak: 'break-all',
                            flexShrink: 0,
                        }, children: attrKey }), !isNested && (_jsx(Text, { sx: {
                            fontSize: 1,
                            fontFamily: 'mono',
                            whiteSpace: 'pre-wrap',
                            wordBreak: 'break-word',
                            color: typeof value === 'string' ? 'accent.emphasis' : 'attention.fg',
                        }, children: typeof value === 'string' ? value : JSON.stringify(value) })), isNested && !open && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', fontFamily: 'mono' }, children: isArray
                            ? `[${value.length} items]`
                            : `{${Object.keys(value).length} keys}` }))] }), isNested && open && (_jsx(Box, { children: isArray
                    ? value.map((item, idx) => (_jsx(AttributeRow, { attrKey: `[${idx}]`, value: item, depth: depth + 1 }, idx)))
                    : Object.entries(value).map(([k, v]) => (_jsx(AttributeRow, { attrKey: k, value: v, depth: depth + 1 }, k))) }))] }));
};
// ── Main component ──────────────────────────────────────────────────
export const OtelSpanDetail = ({ span, traceSpans, onClose, }) => {
    const [activeTab, setActiveTab] = useState('details');
    if (!span) {
        return (_jsx(Box, { sx: { p: 5, color: 'fg.muted', textAlign: 'center' }, children: _jsx(Text, { children: "Select a span to view details." }) }));
    }
    // Separate gen_ai attributes from other attributes
    const genAiAttrs = {};
    const otherAttrs = {};
    if (span.attributes) {
        for (const [k, v] of Object.entries(span.attributes)) {
            if (k.startsWith('gen_ai.') || k.startsWith('model_request')) {
                genAiAttrs[k] = v;
            }
            else {
                otherAttrs[k] = v;
            }
        }
    }
    // Build tree from traceSpans for tree tab
    const tree = traceSpans && traceSpans.length > 0 ? buildSpanTree(traceSpans) : undefined;
    return (_jsxs(Box, { sx: {
            height: '100%',
            overflow: 'auto',
            borderLeft: '1px solid',
            borderColor: 'border.default',
            bg: 'canvas.default',
            display: 'flex',
            flexDirection: 'column',
        }, children: [_jsxs(Box, { sx: {
                    px: 3,
                    py: 2,
                    borderBottom: '1px solid',
                    borderColor: 'border.default',
                    display: 'flex',
                    justifyContent: 'space-between',
                    alignItems: 'center',
                    bg: 'canvas.subtle',
                    flexShrink: 0,
                }, children: [_jsx(Text, { sx: { fontSize: 2, fontWeight: 'bold' }, children: span.span_name }), onClose && (_jsx(IconButton, { icon: XIcon, "aria-label": "Close detail panel", variant: "invisible", size: "small", onClick: onClose }))] }), _jsxs(UnderlineNav, { "aria-label": "Span detail tabs", children: [_jsx(UnderlineNav.Item, { "aria-current": activeTab === 'details' ? 'page' : undefined, onClick: () => setActiveTab('details'), children: "Details" }), tree && (_jsx(UnderlineNav.Item, { "aria-current": activeTab === 'tree' ? 'page' : undefined, onClick: () => setActiveTab('tree'), children: "Trace Tree" })), _jsx(UnderlineNav.Item, { "aria-current": activeTab === 'raw' ? 'page' : undefined, onClick: () => setActiveTab('raw'), children: "Raw Data" })] }), _jsxs(Box, { sx: { flex: 1, overflow: 'auto' }, children: [activeTab === 'details' && (_jsxs(Box, { sx: { p: 3 }, children: [_jsx(MetadataRow, { label: "span_name", value: span.span_name }), _jsx(MetadataRow, { label: "service_name", value: span.service_name }), _jsx(MetadataRow, { label: "otel_scope_name", value: span.otel_scope_name }), _jsx(MetadataRow, { label: "kind", value: span.kind }), _jsx(MetadataRow, { label: "trace_id", value: span.trace_id, mono: true }), _jsx(MetadataRow, { label: "span_id", value: span.span_id, mono: true }), _jsx(MetadataRow, { label: "parent_span_id", value: span.parent_span_id, mono: true }), _jsx(MetadataRow, { label: "duration", value: formatDuration(span.duration_ms) }), _jsx(MetadataRow, { label: "start_time", value: span.start_time, mono: true }), _jsx(MetadataRow, { label: "end_time", value: span.end_time, mono: true }), _jsx(MetadataRow, { label: "status_code", value: span.status_code }), _jsx(MetadataRow, { label: "status_message", value: span.status_message }), _jsx(CollapsibleSection, { title: "Arguments", data: genAiAttrs, defaultOpen: Object.keys(genAiAttrs).length > 0 }), _jsx(CollapsibleSection, { title: "Attributes", data: otherAttrs }), span.events && span.events.length > 0 && (_jsxs(Box, { sx: { mt: 3 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: "Events" }), _jsx(CounterLabel, { children: span.events.length })] }), span.events.map((ev, idx) => (_jsxs(Box, { sx: {
                                            bg: 'canvas.subtle',
                                            borderRadius: 2,
                                            border: '1px solid',
                                            borderColor: 'border.default',
                                            p: 2,
                                            mt: 2,
                                        }, children: [_jsxs(Box, { sx: { display: 'flex', gap: 2 }, children: [_jsx(Label, { children: ev.name }), _jsx(Text, { sx: {
                                                            fontSize: 0,
                                                            color: 'fg.muted',
                                                            fontFamily: 'mono',
                                                        }, children: ev.timestamp })] }), ev.attributes && Object.keys(ev.attributes).length > 0 && (_jsx(Box, { sx: { mt: 1 }, children: Object.entries(ev.attributes).map(([k, v]) => (_jsx(AttributeRow, { attrKey: k, value: v, depth: 0 }, k))) }))] }, idx)))] })), span.links && span.links.length > 0 && (_jsxs(Box, { sx: { mt: 3 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [_jsx(Text, { sx: { fontWeight: 'bold', fontSize: 1 }, children: "Links" }), _jsx(CounterLabel, { children: span.links.length })] }), span.links.map((link, idx) => (_jsxs(Text, { sx: {
                                            display: 'block',
                                            fontSize: 1,
                                            fontFamily: 'mono',
                                            py: 1,
                                            borderBottom: '1px solid',
                                            borderColor: 'border.muted',
                                        }, children: ["trace: ", link.trace_id, " \u2192 span: ", link.span_id] }, idx)))] }))] })), activeTab === 'tree' && tree && (_jsx(OtelSpanTree, { spans: tree, selectedSpanId: span.span_id, defaultExpandDepth: 4 })), activeTab === 'raw' && (_jsx(Box, { as: "pre", sx: {
                            p: 3,
                            fontSize: 0,
                            fontFamily: 'mono',
                            whiteSpace: 'pre-wrap',
                            wordBreak: 'break-word',
                            m: 0,
                            bg: 'canvas.subtle',
                        }, children: JSON.stringify(span, null, 2) }))] })] }));
};
