"use strict";
/**
 * Finding-enrichment adapter — attaches graph context (module
 * membership + blast radius + enclosing symbol) to analyzer findings
 * that carry a `file` (+ optional `line`). Consumed by the detailed
 * report builders for the `vuln` / `test-gaps` / `quality` commands
 * when the user passes `--graph-context`.
 *
 * Per CLAUDE.md Rule 12 this is the only layer (alongside the context
 * hook + dashboard adapter) that loads the graph for finding
 * enrichment; analyzers receive the pre-built `DetailedGraphContext`
 * and never touch graph.json themselves. All graph math stays in
 * `queries.ts:findingContextQuery`.
 *
 * THE CONTRACT IS FAIL-OPEN + ADDITIVE, like the context hook: a
 * missing / corrupt / stale graph degrades to `undefined` (no
 * enrichment), never an error. Findings render exactly as they do
 * today whenever the graph is absent.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.locationKey = locationKey;
exports.buildFindingContextMap = buildFindingContextMap;
exports.formatGraphContextCell = formatGraphContextCell;
exports.graphContextProvenanceLine = graphContextProvenanceLine;
const load_1 = require("./load");
const queries_1 = require("./queries");
const languages_1 = require("../languages");
/** Stable lookup key for a finding location. */
function locationKey(file, line) {
    return typeof line === 'number' ? `${file}:${line}` : file;
}
/**
 * Build per-finding graph context for a list of finding locations.
 * Fail-open: returns `undefined` when the graph can't be loaded.
 *
 * Dedupes identical locations (same file:line surfaced by multiple
 * tools) and budget-caps the work at `maxFindings` unique locations.
 * Only `found: true` contexts are stored — a finding in a file the
 * graph never parsed contributes no key, keeping the payload lean on
 * repos where graphify covers only part of the tree.
 */
function buildFindingContextMap(cwd, locations, opts = {}) {
    const graph = (0, load_1.tryLoadGraph)(cwd);
    if (!graph)
        return undefined;
    const max = opts.maxFindings ?? 200;
    const contexts = {};
    let enriched = 0;
    for (const loc of locations) {
        if (enriched >= max)
            break;
        const key = locationKey(loc.file, loc.line);
        if (key in contexts)
            continue;
        const ctx = (0, queries_1.findingContextQuery)(graph, loc.file, loc.line, {
            topCallerFiles: opts.topCallerFiles,
        });
        enriched++;
        if (!ctx.found)
            continue;
        // Stamp the file's call-graph reliability (Rule 6: the fact comes
        // from the language pack, not a hardcoded table here). Only record
        // the non-default values to keep the payload lean — absent ⇒ 'full'.
        const rel = (0, languages_1.languageForFile)(loc.file)?.callGraphReliability;
        contexts[key] = rel && rel !== 'full' ? { ...ctx, callGraphReliability: rel } : ctx;
    }
    return {
        generatedAt: graph.meta.generatedAt,
        truncated: graph.meta.truncated,
        contexts,
    };
}
/**
 * Compact one-cell rendering for a markdown table: `role · N caller
 * files`. Returns `—` when there's no context for the location (file
 * not in the graph). Used by the detailed report renderers.
 */
function formatGraphContextCell(ctx) {
    if (!ctx || !ctx.found)
        return '—';
    const role = ctx.community?.role ?? 'unclustered';
    // For languages graphify can't resolve call edges for (C#), the
    // caller count is untrustworthy — suppress it rather than print a
    // misleading "0 caller files" (which a fixing agent could read as
    // "safe to change"). The module/role label is still reliable.
    if (ctx.callGraphReliability === 'unreliable') {
        return `${role} · blast radius n/a (call graph)`;
    }
    const n = ctx.blastRadius.callerFiles;
    return `${role} · ${n} caller file${n === 1 ? '' : 's'}`;
}
/**
 * Provenance + honesty line printed above an enriched section so the
 * reader knows the context is a structural hint tied to a specific
 * graph snapshot (and that graphify can conflate same-name symbols).
 */
function graphContextProvenanceLine(gc) {
    const date = gc.generatedAt.slice(0, 10);
    const stale = gc.truncated ? ' (graph truncated — coverage partial)' : '';
    return `_Graph context column from \`.dxkit/reports/graph.json\` (generated ${date}${stale}) — structural hint; blast radius is file-level, same-name symbols may conflate call edges, and it reads \`n/a\` for languages whose call graph graphify can't resolve (a blank is not "0 callers"/"safe to change")._`;
}
//# sourceMappingURL=finding-context.js.map