import { readdir, stat } from "node:fs/promises";
import { basename, join, relative } from "node:path";
const DEFAULT_MAX_FILE_SIZE_BYTES = 256 * 1024;
const DEEP_MAX_FILE_SIZE_BYTES = 1024 * 1024;
const DEFAULT_MAX_ENTRIES = 400;
const DEEP_MAX_ENTRIES = 1200;
const IGNORED_DIRECTORY_REASONS = new Map([
    [".git", "git internals"],
    [".threadwell", "Threadwell runtime state"],
    [".pi", "legacy runtime state; use explicit adoption if supported later"],
    [".engram", "legacy runtime state; use explicit adoption if supported later"],
    ["node_modules", "dependencies"],
    ["target", "build output"],
    ["dist", "build output"],
    ["build", "build output"],
    ["coverage", "test output"],
    [".next", "framework build output"],
    [".turbo", "framework cache"],
    [".cache", "cache"],
    ["vendor", "vendor directory"],
]);
const MANIFEST_KINDS = new Map([
    ["package.json", "node"],
    ["Cargo.toml", "rust"],
    ["pyproject.toml", "python"],
    ["go.mod", "go"],
    ["Makefile", "generic"],
]);
const DOCUMENT_NAMES = new Set(["README.md", "CONTRIBUTING.md", "CHANGELOG.md", "LICENSE", "CONTEXT.md"]);
const CONTINUITY_NAMES = new Set(["STATE.md", "ACTIVE_QUEUE.md", "AUTONOMOUS_EXECUTION.md"]);
const SOURCE_ROOT_NAMES = new Set(["src", "lib", "app", "packages", "crates", "npm"]);
const TEST_ROOT_NAMES = new Set(["test", "tests", "spec", "__tests__"]);
export async function collectProjectInventory(cwd, options) {
    const maxEntries = options.deep ? DEEP_MAX_ENTRIES : DEFAULT_MAX_ENTRIES;
    const maxFileSizeBytes = options.deep ? DEEP_MAX_FILE_SIZE_BYTES : DEFAULT_MAX_FILE_SIZE_BYTES;
    const state = {
        cwd,
        maxEntries,
        maxFileSizeBytes,
        visitedEntries: 0,
        rootEntries: [],
        manifests: [],
        documents: [],
        sourceRoots: [],
        testRoots: [],
        continuityFiles: [],
        adrFiles: [],
        skipped: [],
    };
    await scanDirectory(state, cwd, 0);
    const projectType = detectProjectType(state.manifests);
    return {
        cwd,
        projectType,
        confidence: detectConfidence(state.manifests, state.documents, state.sourceRoots),
        rootEntries: state.rootEntries.sort(comparePath),
        manifests: sortFileSummaries(state.manifests),
        documents: sortFileSummaries(state.documents),
        sourceRoots: state.sourceRoots.sort(comparePath),
        testRoots: state.testRoots.sort(comparePath),
        continuityFiles: sortFileSummaries(state.continuityFiles),
        adrFiles: sortFileSummaries(state.adrFiles),
        skipped: state.skipped.sort((a, b) => comparePath(a.path, b.path)),
    };
}
async function scanDirectory(state, absoluteDirectory, depth) {
    if (state.visitedEntries >= state.maxEntries)
        return;
    let entries;
    try {
        entries = await readdir(absoluteDirectory, { withFileTypes: true });
    }
    catch {
        state.skipped.push({ path: toProjectPath(state.cwd, absoluteDirectory), reason: "unreadable directory" });
        return;
    }
    for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
        if (state.visitedEntries >= state.maxEntries) {
            state.skipped.push({ path: toProjectPath(state.cwd, absoluteDirectory), reason: "entry limit reached" });
            return;
        }
        state.visitedEntries += 1;
        const absolutePath = join(absoluteDirectory, entry.name);
        const projectPath = toProjectPath(state.cwd, absolutePath);
        if (depth === 0)
            state.rootEntries.push(entry.name);
        if (entry.isDirectory()) {
            const ignoredReason = IGNORED_DIRECTORY_REASONS.get(entry.name);
            if (ignoredReason) {
                state.skipped.push({ path: projectPath, reason: ignoredReason });
                continue;
            }
            recordDirectory(state, projectPath, entry.name);
            if (depth < 2)
                await scanDirectory(state, absolutePath, depth + 1);
            continue;
        }
        if (!entry.isFile()) {
            state.skipped.push({ path: projectPath, reason: "not a regular file" });
            continue;
        }
        await recordFile(state, absolutePath, projectPath);
    }
}
function recordDirectory(state, projectPath, name) {
    if (SOURCE_ROOT_NAMES.has(name) && !state.sourceRoots.includes(projectPath))
        state.sourceRoots.push(projectPath);
    if (TEST_ROOT_NAMES.has(name) && !state.testRoots.includes(projectPath))
        state.testRoots.push(projectPath);
}
async function recordFile(state, absolutePath, projectPath) {
    let stats;
    try {
        stats = await stat(absolutePath);
    }
    catch {
        state.skipped.push({ path: projectPath, reason: "unreadable file" });
        return;
    }
    if (stats.size > state.maxFileSizeBytes) {
        state.skipped.push({ path: projectPath, reason: `file larger than ${state.maxFileSizeBytes} bytes` });
        return;
    }
    const name = basename(projectPath);
    const summary = { path: projectPath, sizeBytes: stats.size };
    const manifestKind = MANIFEST_KINDS.get(name);
    if (manifestKind)
        state.manifests.push({ ...summary, kind: manifestKind });
    if (DOCUMENT_NAMES.has(name) ||
        name.endsWith("_PLAN.md") ||
        name.endsWith("PLAN.md") ||
        projectPath.startsWith("docs/")) {
        state.documents.push(summary);
    }
    if (CONTINUITY_NAMES.has(name))
        state.continuityFiles.push(summary);
    if (projectPath.startsWith("docs/adr/"))
        state.adrFiles.push(summary);
}
function detectProjectType(manifests) {
    const types = [];
    if (manifests.some((manifest) => manifest.kind === "node"))
        types.push("Node/npm-like");
    if (manifests.some((manifest) => manifest.kind === "rust"))
        types.push("Rust/Cargo");
    if (manifests.some((manifest) => manifest.kind === "python"))
        types.push("Python");
    if (manifests.some((manifest) => manifest.kind === "go"))
        types.push("Go");
    return types.length > 0 ? types.join(" + ") : "unknown";
}
function detectConfidence(manifests, documents, sourceRoots) {
    if (manifests.length > 0 && documents.length > 0 && sourceRoots.length > 0)
        return "high";
    if (manifests.length > 0 || documents.length > 0 || sourceRoots.length > 0)
        return "medium";
    return "low";
}
function sortFileSummaries(items) {
    return items.sort((a, b) => comparePath(a.path, b.path));
}
function comparePath(a, b) {
    return a.localeCompare(b);
}
function toProjectPath(cwd, absolutePath) {
    const path = relative(cwd, absolutePath).replaceAll("\\", "/");
    return path === "" ? "." : path;
}
//# sourceMappingURL=project-inventory.js.map