// engine/housekeeping/find-stale-stashes.ts — SPEC-751 / SPEC-782
// Detects git stashes older than maxAgeDays days OR with orphaned base branch/commit.
// By default (includeStashes: false), stashes are NEVER auto-deleted.
import { execFile as execFileCb } from 'node:child_process';
import { promisify } from 'node:util';
const execFile = promisify(execFileCb);
const DEFAULT_MAX_AGE_DAYS = 30;
/** Parse base branch from stash message, e.g. "WIP on feat/foo: abc msg" → "feat/foo" */
function parseBaseBranch(message) {
    const match = /^WIP on (\S+):/.exec(message);
    return match?.[1];
}
/** Returns true if the local branch ref exists. */
async function branchExists(branch, cwd) {
    try {
        await execFile('git', ['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], { cwd });
        return true;
    }
    catch {
        return false;
    }
}
/** Returns the parent commit SHA of a stash ref (the commit the stash was based on). */
async function getStashBaseCommit(stashRef, cwd) {
    try {
        const { stdout } = await execFile('git', ['rev-parse', `${stashRef}^`], { cwd });
        return stdout.trim() || undefined;
    }
    catch {
        return undefined;
    }
}
/** Returns true if baseCommit is an ancestor of develop (already merged). */
async function isMergedIntoDevelop(baseCommit, cwd) {
    try {
        await execFile('git', ['merge-base', '--is-ancestor', baseCommit, 'develop'], { cwd });
        return true;
    }
    catch {
        return false;
    }
}
export async function findStaleStashes(input) {
    const { projectPath, maxAgeDays = DEFAULT_MAX_AGE_DAYS } = input;
    let stashOutput;
    try {
        const { stdout } = await execFile('git', ['stash', 'list', '--format=%gd %ct %s'], {
            cwd: projectPath,
        });
        stashOutput = stdout;
    }
    catch {
        return [];
    }
    const nowSeconds = Math.floor(Date.now() / 1000);
    const maxAgeSeconds = maxAgeDays * 86400;
    const stale = [];
    for (const line of stashOutput.trim().split('\n').filter(Boolean)) {
        // Format: "stash@{N} <unix_timestamp> <message>"
        const match = /^(stash@\{\d+\})\s+(\d+)\s+(.*)$/.exec(line);
        if (!match) {
            continue;
        }
        const ref = match[1] ?? '';
        const createdAt = parseInt(match[2] ?? '0', 10);
        const message = match[3] ?? '';
        const ageSeconds = nowSeconds - createdAt;
        const ageDays = Math.floor(ageSeconds / 86400);
        // SPEC-782: orphan detection (independent of age)
        const baseBranch = parseBaseBranch(message);
        if (baseBranch !== undefined) {
            const exists = await branchExists(baseBranch, projectPath);
            if (!exists) {
                stale.push({ ref, createdAt, message, ageDays, reason: 'orphan-branch' });
                continue;
            }
            // Branch exists — check if base commit is already merged into develop
            const baseCommit = await getStashBaseCommit(ref, projectPath);
            if (baseCommit !== undefined) {
                const merged = await isMergedIntoDevelop(baseCommit, projectPath);
                if (merged) {
                    stale.push({ ref, createdAt, message, ageDays, reason: 'orphan-merged' });
                    continue;
                }
            }
        }
        // Fallback: age-based rule
        if (ageSeconds >= maxAgeSeconds) {
            stale.push({ ref, createdAt, message, ageDays, reason: 'age' });
        }
    }
    return stale;
}
//# sourceMappingURL=find-stale-stashes.js.map