/**
 * Parse Gateway GET /metrics (Prometheus text) into a compact TUI-friendly snapshot.
 */
function parseMetricLine(line) {
    if (!line || line.startsWith('#'))
        return null;
    const match = line.match(/^([a-zA-Z_:][a-zA-Z0-9_:]*)\s+(.+)$/);
    if (!match)
        return null;
    const [, nameWithLabels, valueStr] = match;
    if (!nameWithLabels || !valueStr)
        return null;
    const value = parseFloat(valueStr.trim().replace(/\r$/, ''));
    if (Number.isNaN(value))
        return null;
    const labelsMatch = nameWithLabels.match(/^([a-zA-Z_:][a-zA-Z0-9_:]*)\{(.+)\}$/);
    if (labelsMatch) {
        const [, name, labelsStr] = labelsMatch;
        if (!name || !labelsStr)
            return { name: nameWithLabels, value };
        const labels = {};
        const labelPairs = labelsStr.match(/([a-zA-Z_][a-zA-Z0-9_]*)="([^"]*)"/g);
        if (labelPairs) {
            for (const pair of labelPairs) {
                const eq = pair.indexOf('=');
                if (eq > 0) {
                    const key = pair.slice(0, eq);
                    const raw = pair.slice(eq + 1);
                    labels[key] = raw.replace(/^"|"$/g, '');
                }
            }
        }
        return { name, value, labels };
    }
    return { name: nameWithLabels, value };
}
function collectByName(text) {
    const out = new Map();
    for (const line of text.split('\n')) {
        const p = parseMetricLine(line);
        if (!p)
            continue;
        const labels = p.labels ?? {};
        if (!out.has(p.name))
            out.set(p.name, []);
        out.get(p.name).push({ value: p.value, labels });
    }
    return out;
}
function sumNamed(m, name) {
    const rows = m.get(name);
    if (!rows)
        return 0;
    return rows.reduce((s, r) => s + r.value, 0);
}
function firstGauge(m, name) {
    const rows = m.get(name);
    if (!rows?.length)
        return 0;
    return rows[0].value;
}
/**
 * Build a compact observability snapshot for the Portal Monitoring TUI.
 */
export function summarizeGatewayPrometheusMetrics(metricsText) {
    const m = collectByName(metricsText);
    const httpRequests = sumNamed(m, 'http_requests_total');
    const httpRequestErrors = sumNamed(m, 'http_request_errors_total');
    const httpErrorRate = httpRequests > 0 ? (httpRequestErrors / httpRequests * 100) : 0;
    const runsCreated = sumNamed(m, 'runs_created_total');
    const runsStarted = sumNamed(m, 'runs_started_total');
    const runsCompleted = sumNamed(m, 'runs_completed_total');
    const runsActive = firstGauge(m, 'runs_active');
    const sseActive = firstGauge(m, 'sse_active_connections');
    const sseMessages = sumNamed(m, 'sse_messages_total');
    const idempotencyStoreSize = firstGauge(m, 'idempotency_store_size');
    // Queue metrics
    const queueJobsWaiting = firstGauge(m, 'queue_jobs_waiting');
    const queueJobsActive = firstGauge(m, 'queue_jobs_active');
    const queueJobsFailed = sumNamed(m, 'queue_jobs_failed_total');
    const shieldDecisions = sumNamed(m, 'shield_decisions_total');
    const shieldBlocks = sumNamed(m, 'shield_blocks_total');
    const shieldMasks = sumNamed(m, 'shield_masks_total');
    const shieldRewrites = sumNamed(m, 'shield_rewrites_total');
    const durSum = sumNamed(m, 'http_request_duration_seconds_sum');
    const durCount = sumNamed(m, 'http_request_duration_seconds_count');
    let httpLatencyAvgMs = null;
    if (durCount > 0 && Number.isFinite(durSum)) {
        httpLatencyAvgMs = Math.round((durSum / durCount) * 1000);
    }
    const routeMap = new Map();
    for (const row of m.get('http_requests_total') ?? []) {
        const route = row.labels.route ?? '(unknown)';
        const method = row.labels.method ?? '';
        const key = `${method} ${route}`.trim();
        routeMap.set(key, (routeMap.get(key) ?? 0) + row.value);
    }
    const topRoutes = [...routeMap.entries()]
        .map(([key, count]) => ({ key, count }))
        .sort((a, b) => b.count - a.count)
        .slice(0, 14);
    const totals = {
        httpRequests,
        httpRequestErrors,
        httpErrorRate,
        runsCreated,
        runsStarted,
        runsCompleted,
        runsActive,
        sseActive,
        sseMessages,
        idempotencyStoreSize,
        httpLatencyAvgMs,
        queueJobsWaiting,
        queueJobsActive,
        queueJobsFailed,
        shieldDecisions,
        shieldBlocks,
        shieldMasks,
        shieldRewrites,
    };
    const statsLines = [
        `HTTP requests total     ${httpRequests.toLocaleString()}`,
        `HTTP request errors     ${httpRequestErrors.toLocaleString()} (${httpErrorRate.toFixed(2)}%)`,
        `Avg HTTP latency        ${httpLatencyAvgMs === null ? '—' : `${httpLatencyAvgMs.toLocaleString()} ms`}`,
        `Runs created / started  ${runsCreated.toLocaleString()} / ${runsStarted.toLocaleString()}`,
        `Runs completed          ${runsCompleted.toLocaleString()}`,
        `Runs active (gauge)     ${runsActive.toLocaleString()}`,
        `SSE active connections  ${sseActive.toLocaleString()}`,
        `SSE messages total      ${sseMessages.toLocaleString()}`,
        `Queue jobs waiting      ${queueJobsWaiting.toLocaleString()}`,
        `Queue jobs active       ${queueJobsActive.toLocaleString()}`,
        `Queue jobs failed       ${queueJobsFailed.toLocaleString()}`,
        `Idempotency keys (store) ${idempotencyStoreSize.toLocaleString()}`,
        '',
        'Shield (4Runr safety layer)',
        `Shield decisions total   ${shieldDecisions.toLocaleString()}`,
        `Shield blocks total      ${shieldBlocks.toLocaleString()}`,
        `Shield masks total       ${shieldMasks.toLocaleString()}`,
        `Shield rewrites total    ${shieldRewrites.toLocaleString()}`,
    ];
    const routeLines = topRoutes.map((r) => {
        const k = r.key.length > 36 ? `${r.key.slice(0, 33)}...` : r.key;
        return `${k.padEnd(36)} ${r.count.toLocaleString().padStart(10)}`;
    });
    return {
        snapshotAt: new Date().toISOString(),
        totals,
        topRoutes,
        statsLines,
        routeLines,
    };
}
/** Labeled Shield counters from Prometheus — for the Shield monitoring dashboard. */
export function summarizeShieldPrometheusBreakdown(metricsText) {
    const m = collectByName(metricsText);
    const totals = {
        decisions: sumNamed(m, 'shield_decisions_total'),
        blocks: sumNamed(m, 'shield_blocks_total'),
        masks: sumNamed(m, 'shield_masks_total'),
        rewrites: sumNamed(m, 'shield_rewrites_total'),
    };
    const blockRows = [];
    for (const row of m.get('shield_blocks_total') ?? []) {
        const stage = row.labels.stage ?? '?';
        const reason = row.labels.reason ?? '?';
        blockRows.push({ key: `${stage}/${reason}`, count: Math.round(row.value) });
    }
    blockRows.sort((a, b) => b.count - a.count);
    const maskRows = [];
    for (const row of m.get('shield_masks_total') ?? []) {
        const stage = row.labels.stage ?? '?';
        const type = row.labels.type ?? '?';
        maskRows.push({ key: `${stage}/${type}`, count: Math.round(row.value) });
    }
    maskRows.sort((a, b) => b.count - a.count);
    const decisionRows = [];
    for (const row of m.get('shield_decisions_total') ?? []) {
        const stage = row.labels.stage ?? '?';
        const action = row.labels.action ?? '?';
        decisionRows.push({ key: `${stage}/${action}`, count: Math.round(row.value) });
    }
    decisionRows.sort((a, b) => b.count - a.count);
    const breakdownLines = [];
    if (blockRows.length) {
        breakdownLines.push('Blocks by stage/reason');
        for (const r of blockRows.slice(0, 8)) {
            breakdownLines.push(`  ${r.key.padEnd(22)} ${r.count.toLocaleString()}`);
        }
    }
    if (maskRows.length) {
        breakdownLines.push('Masks by stage/type');
        for (const r of maskRows.slice(0, 6)) {
            breakdownLines.push(`  ${r.key.padEnd(22)} ${r.count.toLocaleString()}`);
        }
    }
    if (decisionRows.length) {
        breakdownLines.push('Decisions by stage/action');
        for (const r of decisionRows.slice(0, 8)) {
            breakdownLines.push(`  ${r.key.padEnd(22)} ${r.count.toLocaleString()}`);
        }
    }
    if (breakdownLines.length === 0) {
        breakdownLines.push('No labeled Shield metrics yet — waiting for run traffic.');
    }
    return { totals, blockRows, maskRows, decisionRows, breakdownLines };
}
//# sourceMappingURL=gateway-observability.js.map