import { execFile } from "node:child_process";
import { access } from "node:fs/promises";
import { resolve } from "node:path";
import { evaluateSnapshotFile, normalizeProjectRelativePath } from "./snapshot-policy.js";
export async function detectChangedFiles(cwd, options = {}) {
    const byPath = new Map();
    for (const candidatePath of options.candidatePaths ?? []) {
        const relativePath = normalizeProjectRelativePath(cwd, candidatePath);
        if (!relativePath)
            continue;
        const decision = await evaluateSnapshotFile(cwd, relativePath, options.policy);
        if (!decision.ok && decision.reason === "excluded-path")
            continue;
        byPath.set(relativePath, {
            path: relativePath,
            status: decision.ok ? "modified" : decision.reason === "missing" ? "deleted" : "skipped",
            detectSource: "candidate",
            reason: decision.ok ? undefined : decision.reason,
        });
    }
    for (const file of await detectGitStatusFiles(cwd, options.policy)) {
        const existing = byPath.get(file.path);
        byPath.set(file.path, existing ? { ...file, detectSource: existing.detectSource } : file);
    }
    return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
}
async function detectGitStatusFiles(cwd, policy) {
    const result = await execFileUtf8("git", ["--no-optional-locks", "status", "--porcelain=v1", "--untracked-files=all"], cwd);
    if (result.exitCode !== 0)
        return [];
    const files = [];
    for (const line of result.stdout.split(/\r?\n/u)) {
        if (line.length < 4)
            continue;
        const indexStatus = line[0] ?? " ";
        const worktreeStatus = line[1] ?? " ";
        const rawPath = parseGitStatusPath(line.slice(3));
        const relativePath = normalizeProjectRelativePath(cwd, rawPath);
        if (!relativePath)
            continue;
        const decision = await evaluateSnapshotFile(cwd, relativePath, policy);
        if (!decision.ok && decision.reason === "excluded-path")
            continue;
        files.push({
            path: relativePath,
            status: await classifyGitStatusPath(cwd, relativePath, indexStatus, worktreeStatus),
            detectSource: "git-status",
        });
    }
    return files;
}
function parseGitStatusPath(rawPath) {
    const renameSeparator = " -> ";
    const pathText = rawPath.includes(renameSeparator)
        ? rawPath.slice(rawPath.lastIndexOf(renameSeparator) + renameSeparator.length)
        : rawPath;
    return pathText.replace(/^"|"$/g, "");
}
async function classifyGitStatusPath(cwd, relativePath, indexStatus, worktreeStatus) {
    if (indexStatus === "?" && worktreeStatus === "?")
        return "created";
    if (indexStatus === "D" || worktreeStatus === "D")
        return "deleted";
    if (await fileExists(resolve(cwd, relativePath)))
        return "modified";
    return "deleted";
}
async function fileExists(path) {
    try {
        await access(path);
        return true;
    }
    catch {
        return false;
    }
}
function execFileUtf8(command, args, cwd) {
    return new Promise((resolvePromise) => {
        execFile(command, args, { cwd, encoding: "utf8" }, (error, stdout, stderr) => {
            const exitCode = typeof error?.code === "number" ? error.code : error ? 1 : 0;
            resolvePromise({ exitCode, stdout, stderr });
        });
    });
}
//# sourceMappingURL=snapshot-change-detector.js.map