// engine/session-safeguard/autopush.ts — SPEC-585: Safe ff-only git push helper
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
const PROTECTED_BRANCHES = new Set(['main', 'master', 'develop']);
/** Resolve the current branch name for the given projectPath. */
async function getCurrentBranch(projectPath) {
    try {
        const { stdout } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
            cwd: projectPath,
        });
        const branch = stdout.trim();
        return branch === '' || branch === 'HEAD' ? null : branch;
    }
    catch {
        return null;
    }
}
/** Count commits ahead of upstream on the given branch. */
async function countUnpushedCommits(projectPath, branch) {
    try {
        const upstream = `origin/${branch}`;
        const { stdout } = await execFileAsync('git', ['log', `${upstream}..HEAD`, '--oneline'], {
            cwd: projectPath,
        });
        const lines = stdout.trim().split('\n').filter(Boolean);
        return lines.length;
    }
    catch {
        return null;
    }
}
/** Detect whether a branch matches an allowed prefix pattern (e.g. "feat/*"). */
function matchesBranchPattern(branch, patterns) {
    for (const pattern of patterns) {
        if (pattern.endsWith('/*')) {
            const prefix = pattern.slice(0, -2);
            if (branch.startsWith(`${prefix}/`)) {
                return true;
            }
        }
        else if (branch === pattern) {
            return true;
        }
    }
    return false;
}
/**
 * Push the current branch to origin using ff-only.
 * Never force-pushes. Returns structured result with reason on failure.
 */
export async function safeAutopush(projectPath, allowedPatterns = ['feat/*', 'fix/*', 'chore/*']) {
    const branch = await getCurrentBranch(projectPath);
    if (!branch) {
        return {
            pushed: false,
            reason: 'no-upstream',
            message: 'Could not determine current branch. Ensure you are in a git repository.',
        };
    }
    if (PROTECTED_BRANCHES.has(branch)) {
        return {
            pushed: false,
            reason: 'disabled',
            message: `autopush skipped on protected branch ${branch}`,
        };
    }
    if (allowedPatterns.length > 0 && !matchesBranchPattern(branch, allowedPatterns)) {
        return {
            pushed: false,
            reason: 'disabled',
            message: `autopush skipped: branch "${branch}" does not match allowed patterns (${allowedPatterns.join(', ')})`,
        };
    }
    const commitCount = await countUnpushedCommits(projectPath, branch);
    if (commitCount === null) {
        return {
            pushed: false,
            reason: 'no-upstream',
            message: `No upstream tracking found for branch "${branch}". Run: git push --set-upstream origin ${branch}`,
        };
    }
    if (commitCount === 0) {
        return {
            pushed: false,
            reason: 'nothing-to-push',
            message: `Branch "${branch}" is up to date with origin/${branch}`,
        };
    }
    try {
        await execFileAsync('git', ['push', '--ff-only', 'origin', branch], { cwd: projectPath });
        return { pushed: true, commitsPushed: commitCount, ref: `origin/${branch}` };
    }
    catch (err) {
        const errWithStderr = err;
        const stderr = err instanceof Error && 'stderr' in err ? (errWithStderr.stderr ?? '') : String(err);
        if (stderr.includes('non-fast-forward') || stderr.includes('rejected')) {
            return {
                pushed: false,
                reason: 'diverged',
                message: `Branch "${branch}" has diverged from origin/${branch}. ` +
                    'Resolve with: git pull --rebase origin ' +
                    branch +
                    ', then push manually.',
            };
        }
        if (stderr.includes('no upstream') || stderr.includes('has no upstream')) {
            return {
                pushed: false,
                reason: 'no-upstream',
                message: `No upstream tracking for "${branch}". Run: git push --set-upstream origin ${branch}`,
            };
        }
        return {
            pushed: false,
            reason: 'no-upstream',
            message: `Push failed for "${branch}": ${stderr || 'unknown error'}`,
        };
    }
}
//# sourceMappingURL=autopush.js.map