import * as fs from 'node:fs';
import * as path from 'node:path';
import { resolveTsconfigAlias } from './tsconfig-paths.js';
export function buildImportGraph(records) {
    const nodes = new Set();
    const outgoing = new Map();
    const incoming = new Map();
    for (const r of records) {
        nodes.add(r.source);
        if (!r.target)
            continue;
        nodes.add(r.target);
        addEdge(outgoing, r.source, r.target);
        addEdge(incoming, r.target, r.source);
    }
    return { nodes, outgoing, incoming };
}
function addEdge(map, from, to) {
    const s = map.get(from) ?? new Set();
    s.add(to);
    map.set(from, s);
}
// Resolve TS import specifier → workspace-relative path, or null for
// node_modules / virtual. Tries relative, absolute, then tsconfig paths aliases.
export function resolveImport(workspaceRoot, sourceFile, specifier, tsconfigPaths) {
    const sourceAbs = path.isAbsolute(sourceFile) ? sourceFile : path.join(workspaceRoot, sourceFile);
    // Relative + absolute path resolution path.
    if (specifier.startsWith('.') || specifier.startsWith('/')) {
        const baseDir = path.dirname(sourceAbs);
        const target = specifier.startsWith('/') ? specifier : path.resolve(baseDir, specifier);
        const candidates = [
            target,
            `${target}.ts`,
            `${target}.tsx`,
            path.join(target, 'index.ts'),
            path.join(target, 'index.tsx'),
            `${target}.d.ts`,
            path.join(target, 'index.d.ts'),
        ];
        if (target.endsWith('.js')) {
            candidates.push(target.slice(0, -3) + '.ts', target.slice(0, -3) + '.tsx');
        }
        for (const c of candidates) {
            if (fs.existsSync(c) && fs.statSync(c).isFile()) {
                return path.relative(workspaceRoot, c);
            }
        }
        return null;
    }
    // Bare specifier: try tsconfig path aliases (`@/foo`, `~/bar`, `@app/lib`).
    if (tsconfigPaths) {
        const hit = resolveTsconfigAlias(tsconfigPaths, specifier);
        if (hit)
            return path.relative(workspaceRoot, hit);
    }
    return null;
}
/** BFS reachability from a set of entry-point files. Returns the reachable set. */
export function reachableFrom(graph, entryPoints) {
    const reachable = new Set();
    const queue = [];
    for (const e of entryPoints) {
        if (graph.nodes.has(e) && !reachable.has(e)) {
            reachable.add(e);
            queue.push(e);
        }
    }
    while (queue.length > 0) {
        const cur = queue.shift();
        const out = graph.outgoing.get(cur);
        if (!out)
            continue;
        for (const next of out) {
            if (reachable.has(next))
                continue;
            reachable.add(next);
            queue.push(next);
        }
    }
    return reachable;
}
//# sourceMappingURL=import-graph.js.map