// engine/workflow-validator/git-flow.ts — SPEC-053
// Git Flow violation detection: uncommitted-on-main, feature-from-main, develop-behind-main.
import { execSync } from 'node:child_process';
// === Helpers ===
function runGit(args, cwd) {
    try {
        return execSync(`git ${args}`, {
            cwd,
            encoding: 'utf-8',
            stdio: ['pipe', 'pipe', 'pipe'],
        }).trim();
    }
    catch {
        return '';
    }
}
function getCurrentBranch(projectPath) {
    return runGit('rev-parse --abbrev-ref HEAD', projectPath);
}
function hasUncommittedChanges(projectPath) {
    const output = runGit('status --porcelain', projectPath);
    return output.length > 0;
}
function getBranchBase(projectPath, branch) {
    // Find the merge-base of the branch with main
    const mainCommit = runGit('rev-parse main', projectPath);
    if (!mainCommit) {
        return '';
    }
    return runGit(`merge-base ${branch} main`, projectPath);
}
function getDevelopBase(projectPath, branch) {
    const developCommit = runGit('rev-parse develop', projectPath);
    if (!developCommit) {
        return '';
    }
    return runGit(`merge-base ${branch} develop`, projectPath);
}
function isBranchAheadOf(projectPath, base, target) {
    // Returns true if target is ahead of base (base behind target)
    const commits = runGit(`log ${base}..${target} --oneline`, projectPath);
    return commits.length > 0;
}
// === C1: Uncommitted changes on main ===
function checkUncommittedOnMain(projectPath) {
    const branch = getCurrentBranch(projectPath);
    if (branch !== 'main') {
        return null;
    }
    if (!hasUncommittedChanges(projectPath)) {
        return null;
    }
    return {
        type: 'uncommitted-on-main',
        message: 'HEAD is on main with uncommitted changes that belong on a feature branch.',
        suggestion: 'Run: git stash && git checkout -b feat/<your-feature> && git stash pop',
    };
}
// === C2: Feature branch created from main instead of develop ===
function checkFeatureFromMain(projectPath) {
    const branch = getCurrentBranch(projectPath);
    if (branch === 'main' || branch === 'develop') {
        return null;
    }
    if (!branch.startsWith('feat/') && !branch.startsWith('fix/')) {
        return null;
    }
    const branchBaseFromMain = getBranchBase(projectPath, branch);
    const branchBaseFromDevelop = getDevelopBase(projectPath, branch);
    if (!branchBaseFromMain || !branchBaseFromDevelop) {
        return null;
    }
    // If the merge-base with main equals the merge-base with develop, branch was from develop (good)
    // If merge-base with main is NEWER than merge-base with develop, branch was from main
    const mainHead = runGit('rev-parse main', projectPath);
    if (branchBaseFromMain === mainHead) {
        return {
            type: 'feature-from-main',
            message: `Feature branch '${branch}' was created from main instead of develop.`,
            suggestion: `Rebase onto develop: git rebase --onto develop main ${branch}`,
        };
    }
    return null;
}
// === C3: develop is behind main ===
function checkDevelopBehindMain(projectPath) {
    const mainExists = runGit('rev-parse --verify main', projectPath);
    const developExists = runGit('rev-parse --verify develop', projectPath);
    if (!mainExists || !developExists) {
        return null;
    }
    const behind = isBranchAheadOf(projectPath, 'develop', 'main');
    if (!behind) {
        return null;
    }
    return {
        type: 'develop-behind-main',
        message: 'develop is behind main — needs fast-forward sync.',
        suggestion: 'Run: git checkout develop && git merge --ff-only main && git push origin develop',
    };
}
// === Public API ===
export function validateGitFlow(projectPath) {
    const violations = [];
    const v1 = checkUncommittedOnMain(projectPath);
    if (v1) {
        violations.push(v1);
    }
    const v2 = checkFeatureFromMain(projectPath);
    if (v2) {
        violations.push(v2);
    }
    const v3 = checkDevelopBehindMain(projectPath);
    if (v3) {
        violations.push(v3);
    }
    return { valid: violations.length === 0, violations };
}
//# sourceMappingURL=git-flow.js.map