"use strict";
/**
 * tree-builder.ts
 * Build entity hierarchy and span tree structures for frontend display
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildEntityHierarchy = buildEntityHierarchy;
exports.buildSpanTree = buildSpanTree;
/**
 * Build entity hierarchy tree from agents, tools, models, and spans
 */
function buildEntityHierarchy(agents, tools, models, spans) {
    const spanMap = new Map(spans.map((s) => [s.id, s]));
    const entityBySpanId = new Map();
    // Create nodes for all entities
    for (const agent of agents) {
        const span = spanMap.get(agent.id);
        if (span) {
            const agentDescription = agent.description ?? agent.role ?? agent.goal ?? agent.backstory;
            entityBySpanId.set(agent.id, {
                id: agent.id,
                type: 'agent',
                name: agent.name,
                ...(agentDescription != null && String(agentDescription).trim() !== ''
                    ? { description: String(agentDescription) }
                    : {}),
                spanId: agent.id,
                parentSpanId: span.parentSpanId,
                duration: span.duration / 1000000,
                tokens: agent.cumulativeTokenUsage?.totalTokens,
                ttftList: agent.ttft,
                cumulativeDurationMs: agent.cumulativeDurationMs,
                cumulativeTokenUsage: agent.cumulativeTokenUsage,
                children: [],
                depth: 0,
                toolDefinations: agent.toolDefinitions,
            });
        }
    }
    for (const tool of tools) {
        const span = spanMap.get(tool.spanId);
        if (span) {
            entityBySpanId.set(tool.spanId, {
                id: tool.spanId,
                type: 'tool',
                name: tool.name,
                description: tool.description,
                spanId: tool.spanId,
                parentSpanId: span.parentSpanId,
                duration: tool.duration / 1000000,
                tokens: tool.cumulativeTokenUsage?.totalTokens ?? tool.tokenUsage?.totalTokens,
                ttftList: tool.ttft,
                cumulativeDurationMs: tool.cumulativeDurationMs,
                cumulativeTokenUsage: tool.cumulativeTokenUsage,
                children: [],
                depth: 0,
                argument_defination: tool.argument_defination,
                argument: tool.argument,
                output: tool.output,
            });
        }
    }
    for (const model of models) {
        const span = spanMap.get(model.spanId);
        if (span) {
            const ttft = span.attributes?.['gen_ai.server.time_to_first_token']
                ? parseFloat(String(span.attributes['gen_ai.server.time_to_first_token']))
                : undefined;
            entityBySpanId.set(model.spanId, {
                id: model.spanId,
                type: 'model',
                name: model.modelName,
                spanId: model.spanId,
                parentSpanId: span.parentSpanId,
                duration: span.duration / 1000000,
                tokens: model.tokenUsage?.totalTokens,
                ttft,
                children: [],
                depth: 0,
            });
        }
    }
    // Resolve "semantic" parent for an entity
    const resolveParentEntitySpanId = (spanId) => {
        const start = spanMap.get(spanId);
        let pid = start?.parentSpanId ?? null;
        const seen = new Set();
        while (pid && !entityBySpanId.has(pid)) {
            if (seen.has(pid))
                return null;
            seen.add(pid);
            const p = spanMap.get(pid);
            pid = p?.parentSpanId ?? null;
        }
        return pid && entityBySpanId.has(pid) ? pid : null;
    };
    // Agent→tools: when a framework sets agent.metadata.invokedToolSpanIds, use it so tools appear under agents
    const toolToAgentSpanId = new Map();
    for (const agent of agents) {
        const invokedIds = agent.metadata?.invokedToolSpanIds;
        if (Array.isArray(invokedIds)) {
            for (const tid of invokedIds) {
                if (typeof tid === 'string' && entityBySpanId.has(tid))
                    toolToAgentSpanId.set(tid, agent.id);
            }
        }
    }
    // Build parent-child relationships
    const childrenMap = new Map();
    const roots = [];
    for (const entity of entityBySpanId.values()) {
        let parentEntityId = null;
        if (entity.type === 'tool') {
            parentEntityId = toolToAgentSpanId.get(entity.spanId) ?? null;
        }
        if (parentEntityId == null) {
            parentEntityId = resolveParentEntitySpanId(entity.spanId);
        }
        if (!parentEntityId) {
            roots.push(entity);
        }
        else {
            const arr = childrenMap.get(parentEntityId);
            if (arr)
                arr.push(entity);
            else
                childrenMap.set(parentEntityId, [entity]);
        }
    }
    // Reassign models from agent to tool when model started after a tool ended (same agent).
    // In frameworks like CrewAI, model spans are direct children of the agent, so without this
    // tools would have no model children in the detail view.
    const timestampMs = (spanId) => {
        const span = spanMap.get(spanId);
        if (!span?.timestamp)
            return 0;
        const t = span.timestamp;
        if (typeof t === 'number')
            return t <= 1e12 ? t : t / 1e6;
        const parsed = Date.parse(t);
        return Number.isFinite(parsed) ? parsed : 0;
    };
    const endMs = (spanId) => {
        const span = spanMap.get(spanId);
        const start = timestampMs(spanId);
        const dur = span?.duration;
        if (dur == null)
            return start;
        const durMs = typeof dur === 'number' ? (dur > 1e9 ? dur / 1e6 : dur) : 0;
        return start + durMs;
    };
    for (const entity of entityBySpanId.values()) {
        if (entity.type !== 'agent')
            continue;
        const agentChildren = childrenMap.get(entity.spanId);
        if (!agentChildren?.length)
            continue;
        const agentTools = agentChildren.filter((c) => c.type === 'tool');
        const agentModels = agentChildren.filter((c) => c.type === 'model');
        if (agentTools.length === 0 || agentModels.length === 0)
            continue;
        const toolsByEnd = agentTools.slice().sort((a, b) => endMs(a.spanId) - endMs(b.spanId));
        for (const modelEntity of agentModels) {
            const modelStart = timestampMs(modelEntity.spanId);
            let bestTool = null;
            let bestEnd = -1;
            for (const t of toolsByEnd) {
                const te = endMs(t.spanId);
                if (te <= modelStart && te > bestEnd) {
                    bestEnd = te;
                    bestTool = t;
                }
            }
            if (bestTool) {
                const idx = agentChildren.indexOf(modelEntity);
                if (idx !== -1)
                    agentChildren.splice(idx, 1);
                const toolKids = childrenMap.get(bestTool.spanId);
                if (toolKids)
                    toolKids.push(modelEntity);
                else
                    childrenMap.set(bestTool.spanId, [modelEntity]);
            }
        }
    }
    // Build tree recursively
    const buildNode = (node, depth) => {
        node.depth = depth;
        const children = (childrenMap.get(node.spanId) ?? []).map((child) => buildNode(child, depth + 1));
        node.children = children;
        return node;
    };
    return roots.map((root) => buildNode(root, 0));
}
/**
 * Build span tree from spans
 */
function buildSpanTree(spans) {
    const spanMap = new Map(spans.map((s) => [s.id, s]));
    const childrenMap = new Map();
    const roots = [];
    for (const span of spans) {
        if (!span.parentSpanId || !spanMap.has(span.parentSpanId)) {
            roots.push(span);
        }
        else {
            const arr = childrenMap.get(span.parentSpanId);
            if (arr)
                arr.push(span);
            else
                childrenMap.set(span.parentSpanId, [span]);
        }
    }
    const buildNode = (span, depth) => {
        const children = (childrenMap.get(span.id) ?? []).map((child) => buildNode(child, depth + 1));
        return { span, children, depth };
    };
    return roots.map((root) => buildNode(root, 0));
}
//# sourceMappingURL=treeBuilder.js.map