/*
 * Copyright OpenSearch Contributors
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * Empty accessor — silent zeros. Used when the agent opted out of traces
 * (`useTraces: false`). Reading any accessor returns `0` / `[]` so user
 * matchers in opt-out scenarios don't throw.
 */
export function emptyTracesAccessor() {
    return {
        totalTokens: 0,
        totalCost: 0,
        toolCalls: [],
        spanDuration: () => 0,
        spans: [],
    };
}
/**
 * Loud-failure accessor — every read throws. Used when the agent opted
 * in to traces (`useTraces: true`) but spans were not retrievable. This
 * turns the silent false-pass described in issue #230 into an actionable
 * error, e.g.:
 *
 *   Error: traces fixture unavailable: no spans found for runId=…
 *
 * Construction never throws — only attribute / method access does.
 */
export function unavailableTracesAccessor(reason) {
    const fail = () => {
        throw new Error(`traces fixture unavailable: ${reason}`);
    };
    return {
        get totalTokens() { return fail(); },
        get totalCost() { return fail(); },
        get toolCalls() { return fail(); },
        get spans() { return fail(); },
        spanDuration(_name) { return fail(); },
    };
}
/** Build a TracesAccessor from a flat list of spans. */
export function buildTracesAccessor(spans) {
    let totalTokens = 0;
    let totalCost = 0;
    const toolCalls = [];
    for (const span of spans) {
        const attrs = span.attributes ?? {};
        const promptTokens = pickNumber(attrs, [
            'gen_ai.usage.prompt_tokens',
            'gen_ai.usage.input_tokens',
            'llm.usage.prompt_tokens',
        ]);
        const completionTokens = pickNumber(attrs, [
            'gen_ai.usage.completion_tokens',
            'gen_ai.usage.output_tokens',
            'llm.usage.completion_tokens',
        ]);
        if (promptTokens)
            totalTokens += promptTokens;
        if (completionTokens)
            totalTokens += completionTokens;
        const cost = pickNumber(attrs, [
            'gen_ai.usage.cost_usd',
            'gen_ai.usage.cost',
            'llm.usage.cost_usd',
        ]);
        if (cost)
            totalCost += cost;
        // Tool spans
        const toolName = pickString(attrs, ['gen_ai.tool.name', 'llm.tool.name']);
        if (toolName) {
            toolCalls.push({ name: toolName, durationMs: spanDurationMs(span) });
        }
    }
    return {
        totalTokens,
        totalCost,
        toolCalls,
        spanDuration(name) {
            const hit = spans.find(s => s.name === name);
            return hit ? spanDurationMs(hit) : 0;
        },
        spans,
    };
}
function spanDurationMs(span) {
    const start = span.startTimeUnixNano ?? span.startTime;
    const end = span.endTimeUnixNano ?? span.endTime;
    if (typeof start === 'number' && typeof end === 'number') {
        // Heuristic: nanosecond timestamps when 13+ digits, else ms
        const factor = Math.abs(start) > 1e15 ? 1e6 : 1;
        return Math.max(0, Math.round((end - start) / factor));
    }
    if (typeof span.durationMs === 'number')
        return span.durationMs;
    return 0;
}
function pickNumber(attrs, keys) {
    for (const k of keys) {
        const v = attrs[k];
        if (typeof v === 'number' && Number.isFinite(v))
            return v;
        if (typeof v === 'string' && v.trim() !== '' && Number.isFinite(Number(v)))
            return Number(v);
    }
    return undefined;
}
function pickString(attrs, keys) {
    for (const k of keys) {
        const v = attrs[k];
        if (typeof v === 'string' && v.length > 0)
            return v;
    }
    return undefined;
}
//# sourceMappingURL=traces.js.map