"use strict";
/**
 * Shared helpers for RAG/vector DB parsers.
 * Normalize attributes (OTel DB semantic conventions + vendor-specific) into standard RAGCall fields.
 * Uses Decimal.js for numeric calculations; all numeric fields guard against null/undefined/NaN.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAttr = getAttr;
exports.parseNum = parseNum;
exports.getDurationMs = getDurationMs;
exports.getDurationSeconds = getDurationSeconds;
exports.getSystemName = getSystemName;
exports.getOperationName = getOperationName;
exports.getCollectionName = getCollectionName;
exports.getReturnedRows = getReturnedRows;
exports.getStatus = getStatus;
exports.getTopK = getTopK;
exports.buildRAGCall = buildRAGCall;
exports.ragCallToDetailsCard = ragCallToDetailsCard;
exports.buildTraceRagDetailsCards = buildTraceRagDetailsCards;
const decimal_js_1 = __importDefault(require("decimal.js"));
const DB_SYSTEM = 'db.system';
const DB_SYSTEM_NAME = 'db.system.name';
const DB_OPERATION_NAME = 'db.operation.name';
const DB_CLIENT_OPERATION_DURATION = 'db.client.operation.duration';
const DB_RESPONSE_RETURNED_ROWS = 'db.response.returned_rows';
const DB_COLLECTION_NAME = 'db.collection.name';
const DB_QUERY_SUMMARY = 'db.query.summary';
/** Get first defined value from span attributes (and optional resourceAttributes) for given keys */
function getAttr(span, ...keys) {
    const attrs = span.attributes ?? {};
    const res = span.resourceAttributes ?? {};
    for (const k of keys) {
        const v = attrs[k] ?? res[k];
        if (v !== undefined && v !== null && String(v).trim() !== '')
            return v;
    }
    return undefined;
}
/**
 * Parse number from attribute; returns undefined for null/undefined/NaN/non-finite.
 * Use for optional numeric fields (topK, returnedRows).
 */
function parseNum(value) {
    if (value == null)
        return undefined;
    if (typeof value === 'number') {
        if (!Number.isFinite(value) || Number.isNaN(value))
            return undefined;
        return value;
    }
    const n = Number(String(value).trim());
    if (!Number.isFinite(n) || Number.isNaN(n))
        return undefined;
    return n;
}
/**
 * Duration in milliseconds: prefer db.client.operation.duration, else span.duration.
 * Uses Decimal.js for calculation; returns 0 for null/undefined/NaN.
 * OTel db.client.operation.duration is seconds → multiply by 1000.
 * Span duration in this codebase is nanoseconds → divide by 1e6.
 */
function getDurationMs(span) {
    const attrDuration = getAttr(span, DB_CLIENT_OPERATION_DURATION);
    const attrRaw = parseNum(attrDuration);
    const raw = attrRaw ?? parseNum(span.duration);
    if (raw == null || !Number.isFinite(raw))
        return 0;
    const rawDecimal = new decimal_js_1.default(raw);
    let ms;
    if (attrRaw === undefined) {
        ms = rawDecimal.div(1e6);
    }
    else {
        ms = rawDecimal.mul(1000);
    }
    const result = ms.toNumber();
    return Number.isFinite(result) && !Number.isNaN(result) ? result : 0;
}
/** Duration in seconds (legacy helper). Prefer {@link getDurationMs} for aggregation (observeDashboard expects ms). */
function getDurationSeconds(span) {
    return getDurationMs(span) / 1000;
}
/** db.system.name or db.system */
function getSystemName(span, fallback) {
    const v = getAttr(span, DB_SYSTEM_NAME, DB_SYSTEM);
    return (v != null ? String(v).trim() : fallback) || fallback;
}
/** db.operation.name or span name */
function getOperationName(span) {
    const v = getAttr(span, DB_OPERATION_NAME);
    return (v != null ? String(v).trim() : span.name) || 'unknown';
}
/** db.collection.name */
function getCollectionName(span) {
    const v = getAttr(span, DB_COLLECTION_NAME);
    return v != null ? String(v).trim() : undefined;
}
/**
 * returned_rows: db.response.returned_rows, or estimate from db.query.summary / result-related
 * attributes when they encode actual result count (not request params like top_k).
 * Returns undefined for null/undefined/NaN; only returns finite integers.
 */
function getReturnedRows(span) {
    const direct = parseNum(getAttr(span, DB_RESPONSE_RETURNED_ROWS));
    if (direct != null && Number.isInteger(direct))
        return direct;
    // Prefer result-oriented summary if present (e.g. db.response.summary)
    const resultSummary = getAttr(span, 'db.response.summary', 'db.result.summary');
    if (resultSummary != null && typeof resultSummary === 'string') {
        const fromResult = estimateRowsFromResultSummary(String(resultSummary).trim());
        if (fromResult != null)
            return fromResult;
    }
    const summary = getAttr(span, DB_QUERY_SUMMARY);
    if (summary == null || typeof summary !== 'string')
        return undefined;
    const estimated = estimateRowsFromSummary(summary);
    return estimated != null && Number.isInteger(estimated) ? estimated : undefined;
}
/**
 * Estimate row count only from summaries that describe the result (rows returned), not request params.
 * - SELECT N / FETCH N: in some SQL conventions this is the number of rows returned.
 * - LIMIT N: request max; only use when summary clearly indicates result (e.g. "returned N").
 * Do NOT use top_k, limit (request), or similar as returned rows — they are request parameters.
 */
function estimateRowsFromSummary(summary) {
    const s = summary.trim();
    const selectMatch = s.match(/\b(?:SELECT|FETCH)\s+(\d+)/i);
    if (selectMatch)
        return parseNum(selectMatch[1]);
    const limitMatch = s.match(/\bLIMIT\s+(\d+)/i);
    if (limitMatch)
        return parseNum(limitMatch[1]);
    return undefined;
}
/**
 * Estimate from result-oriented summary (e.g. db.response.summary): "returned N", "matches: N", "count: N".
 */
function estimateRowsFromResultSummary(summary) {
    const s = summary.trim();
    const returnedMatch = s.match(/\breturned\s+(\d+)/i);
    if (returnedMatch)
        return parseNum(returnedMatch[1]);
    const matchesMatch = s.match(/\bmatches?\s*[=:]\s*(\d+)/i);
    if (matchesMatch)
        return parseNum(matchesMatch[1]);
    const countMatch = s.match(/\bcount\s*[=:]\s*(\d+)/i);
    if (countMatch)
        return parseNum(countMatch[1]);
    return undefined;
}
/** Infer status from span status, error events, or attributes */
function getStatus(span) {
    const status = span.status;
    if (status?.code !== undefined) {
        if (status.code === 0 || status.code === 1)
            return 'passed';
        if (status.code === 2)
            return 'error';
        return 'failed';
    }
    const hasError = (span.events ?? []).some((e) => e.name?.toLowerCase().includes('exception') ||
        e.name?.toLowerCase().includes('error')) ||
        (span.attributes ?? {})['error'] === true;
    if (hasError)
        return 'error';
    return 'passed';
}
/** top_k: common attribute names for vector DBs (incl. db.vector.query.top_k for Pinecone/Qdrant). Returns undefined for null/undefined/NaN. */
function getTopK(span) {
    const v = getAttr(span, 'top_k', 'db.vector.query.top_k', 'k', 'limit', 'n_results', 'n', 'db.query.parameters.top_k');
    const n = parseNum(v);
    return n != null && n >= 0 ? n : undefined;
}
/** Build a standard RAGCall from a span and optional overrides. All numeric fields are safe (no NaN/null/undefined). */
function buildRAGCall(span, options) {
    const systemName = options?.systemName ?? getSystemName(span, 'unknown');
    const operationName = options?.operationName ?? getOperationName(span);
    const durationMs = getDurationMs(span);
    const topK = getTopK(span);
    const returnedRows = getReturnedRows(span);
    const collectionName = getCollectionName(span);
    return {
        spanId: span?.id ?? '',
        traceId: span?.traceId ?? '',
        ...(topK != null && Number.isFinite(topK) && !Number.isNaN(topK) && { topK }),
        duration: Number.isFinite(durationMs) && !Number.isNaN(durationMs) ? durationMs : 0,
        systemName: systemName || 'unknown',
        operationName: operationName || 'unknown',
        status: getStatus(span),
        ...(returnedRows != null && Number.isFinite(returnedRows) && !Number.isNaN(returnedRows) && { returnedRows }),
        ...(collectionName != null && collectionName !== '' && { collectionName }),
        ...(span?.timestamp != null && span.timestamp !== '' && { timestamp: span.timestamp }),
    };
}
function formatDurationMs(ms) {
    if (ms == null || !Number.isFinite(ms))
        return '—';
    return `${Number(ms).toFixed(2)} ms`;
}
function formatSpanDurationNs(ns) {
    if (ns == null || !Number.isFinite(ns))
        return '—';
    const ms = ns / 1000000;
    return `${Number(ms).toFixed(2)} ms`;
}
function formatTimestamp(ts) {
    if (!ts || typeof ts !== 'string')
        return '—';
    try {
        const d = new Date(ts);
        return Number.isFinite(d.getTime()) ? d.toISOString() : ts;
    }
    catch {
        return ts;
    }
}
/**
 * Build one display card from a RAGCall and its source span (for serviceName and span duration).
 * All formatting done here; frontend renders as-is.
 */
function ragCallToDetailsCard(call, span) {
    const spanDurationNs = span?.duration;
    const serviceName = span?.serviceName ?? '—';
    return {
        spanId: call.spanId || '—',
        traceId: call.traceId || '—',
        spanDuration: span != null ? formatSpanDurationNs(spanDurationNs ?? 0) : '—',
        operationDuration: formatDurationMs(call.duration),
        operationName: call.operationName || '—',
        systemName: call.systemName || '—',
        serviceName,
        status: call.status ?? '—',
        timestamp: call.timestamp ? formatTimestamp(call.timestamp) : undefined,
        topK: call.topK != null ? String(call.topK) : undefined,
        returnedRows: call.returnedRows != null ? String(call.returnedRows) : undefined,
        collectionName: call.collectionName ?? undefined,
    };
}
/**
 * Convert RAGCall[] to TraceRagDetailsCard[] using spans to fill serviceName and span duration.
 */
function buildTraceRagDetailsCards(ragCalls, spans) {
    const spanById = new Map();
    for (const s of spans) {
        spanById.set(s.id, s);
    }
    return ragCalls.map((call) => ragCallToDetailsCard(call, spanById.get(call.spanId)));
}
//# sourceMappingURL=utils.js.map