// engine/housekeeping/sweep-runner.ts — SPEC-751
// Orchestrates find* functions and optionally executes deletions.
import { execFile as execFileCb } from 'node:child_process';
import { promisify } from 'node:util';
import { findStaleBranches, cleanStaleBackups } from './find-stale-branches.js';
import { findStaleWorktrees } from './find-stale-worktrees.js';
import { findStaleStashes } from './find-stale-stashes.js';
import { appendHousekeepingEntry } from './history-log.js';
const execFile = promisify(execFileCb);
// ---------------------------------------------------------------------------
// Deletion helpers
// ---------------------------------------------------------------------------
async function deleteBranch(projectPath, branchName) {
    try {
        await execFile('git', ['branch', '-D', branchName], { cwd: projectPath });
        return true;
    }
    catch {
        return false;
    }
}
async function removeWorktree(projectPath, worktreePath) {
    try {
        await execFile('git', ['worktree', 'remove', '--force', worktreePath], { cwd: projectPath });
        return true;
    }
    catch {
        return false;
    }
}
async function dropStash(projectPath, stashRef) {
    try {
        await execFile('git', ['stash', 'drop', stashRef], { cwd: projectPath });
        return true;
    }
    catch {
        return false;
    }
}
// ---------------------------------------------------------------------------
// Backup helpers
// ---------------------------------------------------------------------------
async function backupAndDeleteBranch(projectPath, branch) {
    // Write to history BEFORE deleting (for recovery)
    try {
        await appendHousekeepingEntry(projectPath, {
            projectPath,
            kind: 'branch',
            ref: branch.name,
            sha: branch.sha,
        });
    }
    catch {
        // Log failure is non-fatal — continue with deletion
    }
    return deleteBranch(projectPath, branch.name);
}
async function backupAndRemoveWorktree(projectPath, wt) {
    try {
        await appendHousekeepingEntry(projectPath, {
            projectPath,
            kind: 'worktree',
            ref: wt.path,
            sha: wt.branch ?? '',
        });
    }
    catch {
        // non-fatal
    }
    return removeWorktree(projectPath, wt.path);
}
async function backupAndDropStash(projectPath, stash) {
    try {
        await appendHousekeepingEntry(projectPath, {
            projectPath,
            kind: 'stash',
            ref: stash.ref,
            sha: String(stash.createdAt),
        });
    }
    catch {
        // non-fatal
    }
    return dropStash(projectPath, stash.ref);
}
// ---------------------------------------------------------------------------
// Main orchestrator
// ---------------------------------------------------------------------------
export async function runHousekeepingSweep(input) {
    const { projectPath, dryRun = true, aggressive = false, includeStashes = false } = input;
    const sweepAt = new Date().toISOString();
    // Find stale items in parallel
    const [{ stale: staleBranches, skipped }, staleWorktrees, staleStashes] = await Promise.all([
        findStaleBranches({ projectPath, aggressive }),
        findStaleWorktrees({ projectPath }),
        includeStashes ? findStaleStashes({ projectPath }) : Promise.resolve([]),
    ]);
    const report = {
        sweepAt,
        projectPath,
        wouldDelete: {
            branches: staleBranches,
            worktrees: staleWorktrees,
            stashes: staleStashes,
        },
        kept: {
            branches: skipped,
        },
        executed: false,
        deleted: { branches: 0, worktrees: 0, stashes: 0, backups: 0 },
    };
    if (dryRun) {
        return report;
    }
    // Execute deletions
    report.executed = true;
    // Delete branches (sequential — each needs a backup entry first)
    for (const branch of staleBranches) {
        const ok = await backupAndDeleteBranch(projectPath, branch);
        if (ok) {
            report.deleted.branches++;
        }
    }
    // Remove worktrees (sequential)
    for (const wt of staleWorktrees) {
        const ok = await backupAndRemoveWorktree(projectPath, wt);
        if (ok) {
            report.deleted.worktrees++;
        }
    }
    // Drop stashes (sequential — must handle index shifting)
    // Drop in reverse order so indices stay valid
    const reversedStashes = [...staleStashes].reverse();
    for (const stash of reversedStashes) {
        const ok = await backupAndDropStash(projectPath, stash);
        if (ok) {
            report.deleted.stashes++;
        }
    }
    // SPEC-771: clean stale .bak.* backup files from planu/specs/ (best-effort)
    report.deleted.backups = await cleanStaleBackups(projectPath);
    return report;
}
//# sourceMappingURL=sweep-runner.js.map