/**
 * Async file-system walker used by nexus_optimize_tokens and nexus_hypertune_max.
 *
 * Extracted from mcp.ts (Phase 3 split) to keep each file under 1500 LOC.
 * Uses BFS with pLimit(8) concurrency — no sync FS calls, no per-entry stat.
 *
 * Fast path: `git ls-files` for repos (< 300 ms even on large codebases).
 * Fall back to walkDir only when not inside a git repository.
 */
import { promises as fsPromises } from 'fs';
import * as path from 'path';
import { pLimit } from '../../../../engines/util/p-limit.js';
import { execAsync } from '../../../../utils/exec-async.js';
const SKIP = new Set([
    'node_modules', 'dist', '.git', '.next', '.turbo', '.cache',
    'coverage', '.nexus-prime', '.claude', '.vercel', 'build', 'out',
]);
/**
 * Recursively walk `root`, returning all file paths.
 * Directories in SKIP are never descended into.
 */
export async function walkDir(root) {
    const limit = pLimit(8);
    const results = [];
    let queue = [root];
    while (queue.length > 0) {
        const next = [];
        await Promise.all(queue.map((dir) => limit(async () => {
            let entries;
            try {
                entries = await fsPromises.readdir(dir, { withFileTypes: true });
            }
            catch {
                return;
            }
            for (const entry of entries) {
                if (entry.isSymbolicLink())
                    continue;
                if (entry.isDirectory()) {
                    if (SKIP.has(entry.name))
                        continue;
                    next.push(path.join(dir, entry.name));
                }
                else if (entry.isFile()) {
                    results.push(path.join(dir, entry.name));
                }
            }
        })));
        queue = next;
    }
    return results;
}
/**
 * Enumerate TypeScript source files via `git ls-files` (fast path) or
 * recursive walkDir (fallback for non-git directories).
 *
 * `git ls-files` completes in < 300 ms even on large repos; walkDir on a
 * repo with thousands of files can exceed the 25 s MCP handler timeout.
 * Cache keyed on cwd + top-level directory mtime so repeated calls are free.
 */
export async function scanSourceFiles(cwd, scanCache) {
    let topMtime = 0;
    try {
        const stat = await fsPromises.stat(cwd);
        topMtime = stat.mtimeMs;
    }
    catch {
        // missing dir — fall through and return []
    }
    const cached = scanCache.get(cwd);
    if (cached && cached.mtimeMs === topMtime) {
        return cached.files;
    }
    let filtered;
    try {
        // Fast path: git ls-files lists only tracked + untracked (unignored) files.
        // -z uses NUL separators so paths with spaces work correctly.
        const { stdout } = await execAsync('git ls-files --cached --others --exclude-standard -z -- "*.ts"', { cwd, timeout: 8_000 });
        filtered = stdout
            .split('\0')
            .filter(Boolean)
            .map((f) => path.isAbsolute(f) ? f : path.join(cwd, f))
            .filter((f) => !f.endsWith('.d.ts'));
    }
    catch {
        // Not a git repo or git unavailable — fall back to directory walk.
        const files = await walkDir(cwd);
        filtered = files.filter((f) => f.endsWith('.ts') && !f.endsWith('.d.ts'));
    }
    scanCache.set(cwd, { mtimeMs: topMtime, files: filtered });
    return filtered;
}
