import { specStore } from '../../storage/index.js';
import { git, resolveProjectPath, mergeConfig, resolveBaseBranch, countCommitsBehind, detectGitFlowType, } from './git-helpers.js';
import { compactResult, compactError, formatList, formatKeyValue } from '../output-formatter.js';
export async function handleSyncBranch(projectId, config) {
    const projectPath = await resolveProjectPath(projectId);
    const mergedConfig = mergeConfig(config);
    const baseBranch = await resolveBaseBranch(projectPath, mergedConfig.baseBranch);
    if (!baseBranch) {
        return compactError('No base branch found (develop/main/master)');
    }
    const commitsBehinds = await countCommitsBehind(projectPath, baseBranch);
    const gitFlowType = await detectGitFlowType(projectPath);
    // Determine strategy: rebase for feature branches, merge for release/hotfix
    const { stdout: currentBranchOut } = await git(projectPath, [
        'rev-parse',
        '--abbrev-ref',
        'HEAD',
    ]);
    const currentBranch = currentBranchOut.trim();
    const isReleaseBranch = /^(release|hotfix)\//.test(currentBranch);
    const strategy = isReleaseBranch ? 'merge' : 'rebase';
    if (commitsBehinds === 0) {
        return compactResult(`Branch '${currentBranch}' is up to date with '${baseBranch}' (${strategy}, ${gitFlowType})`);
    }
    // Perform fetch
    try {
        await git(projectPath, ['fetch', 'origin', baseBranch]);
    }
    catch {
        // fetch might fail without a remote — continue with local
    }
    // Apply strategy
    const conflictingFiles = [];
    let syncSuccess = true;
    try {
        if (strategy === 'rebase') {
            await git(projectPath, ['rebase', baseBranch]);
        }
        else {
            await git(projectPath, ['merge', '--no-edit', baseBranch]);
        }
    }
    catch {
        syncSuccess = false;
        // Get conflicting files
        try {
            const { stdout: conflictOut } = await git(projectPath, [
                'diff',
                '--name-only',
                '--diff-filter=U',
            ]);
            conflictingFiles.push(...conflictOut.trim().split('\n').filter(Boolean));
        }
        catch {
            // ignore
        }
        // Abort the operation to restore clean state
        try {
            if (strategy === 'rebase') {
                await git(projectPath, ['rebase', '--abort']);
            }
            else {
                await git(projectPath, ['merge', '--abort']);
            }
        }
        catch {
            /* ignore abort failures */
        }
    }
    const syncMsg = syncSuccess
        ? `Successfully synced '${currentBranch}' with '${baseBranch}' via ${strategy}`
        : `${strategy} resulted in conflicts. Resolve files: ${conflictingFiles.join(', ')}`;
    const syncText = [
        syncMsg,
        '',
        formatKeyValue({ strategy, gitFlowType, commitsBehinds: String(commitsBehinds) }),
        ...(conflictingFiles.length > 0 ? ['', formatList(conflictingFiles, 'Conflicting Files')] : []),
    ].join('\n');
    return compactResult(syncText);
}
export async function handleCheckGitflow(projectId, config) {
    const projectPath = await resolveProjectPath(projectId);
    const gitFlowType = await detectGitFlowType(projectPath);
    const mergedConfig = mergeConfig(config);
    const { stdout: currentBranchOut } = await git(projectPath, [
        'rev-parse',
        '--abbrev-ref',
        'HEAD',
    ]);
    const currentBranch = currentBranchOut.trim();
    const violations = [];
    const isFeatureBranch = /^(feat|fix|refactor|infra|docs|project)\//.test(currentBranch);
    if (gitFlowType === 'gitflow' && isFeatureBranch) {
        // In gitflow, feature branches should NOT merge directly to main
        /* v8 ignore start */
        const protectedBranches = mergedConfig.protectedBranches ?? ['main', 'master'];
        const mainBranch = protectedBranches.find((b) => b === 'main' || b === 'master') ?? 'main';
        /* v8 ignore end */
        try {
            await git(projectPath, ['merge-base', '--is-ancestor', currentBranch, mainBranch]);
            violations.push(`Feature branch '${currentBranch}' should merge to 'develop' first, not directly to '${mainBranch}'`);
        }
        catch {
            // Not an ancestor — no violation
        }
    }
    if (gitFlowType === 'trunk-based') {
        // In trunk-based, only validate branch naming
        const conventionPattern = /^(feat|fix|refactor|infra|docs|project)\/SPEC-\d{3}-/;
        if (!conventionPattern.test(currentBranch) && !/^(main|master|develop)$/.test(currentBranch)) {
            violations.push(`Branch '${currentBranch}' does not follow naming convention for trunk-based flow`);
        }
    }
    const gitflowMsg = violations.length === 0
        ? `Branch flow is valid for ${gitFlowType}`
        : `${violations.length} gitflow violation(s) detected`;
    const gitflowText = [
        gitflowMsg,
        '',
        formatKeyValue({
            gitFlowType,
            currentBranch,
            isValid: String(violations.length === 0),
        }),
        ...(violations.length > 0 ? ['', formatList(violations, 'Violations')] : []),
    ].join('\n');
    return compactResult(gitflowText);
}
export async function handleResolveConflict(projectId, files) {
    const projectPath = await resolveProjectPath(projectId);
    // Get conflicting files if not provided
    let conflictingFiles = files ?? [];
    if (conflictingFiles.length === 0) {
        try {
            const { stdout } = await git(projectPath, ['diff', '--name-only', '--diff-filter=U']);
            conflictingFiles = stdout.trim().split('\n').filter(Boolean);
        }
        catch {
            // ignore
        }
    }
    if (conflictingFiles.length === 0) {
        return compactResult('No conflicting files found');
    }
    // For each conflicting file, find specs linked to it
    const allSpecs = await specStore.listSpecs(projectId);
    const activeSpecs = allSpecs.filter((s) => s.status === 'implementing' || s.status === 'approved');
    const conflicts = conflictingFiles.map((file) => {
        // Find specs whose gitBranch contains files matching this path
        const linkedSpecs = activeSpecs.filter((s) => 
        /* v8 ignore start */
        s.gitBranch.includes(file.split('/')[0] ?? '') ||
            /* v8 ignore end */
            s.id === extractSpecFromPath(file));
        return {
            file,
            linkedSpecs: linkedSpecs.map((s) => ({
                specId: s.id,
                title: s.title,
                status: s.status,
                context: `Resolve keeping spec ${s.id} (${s.title}) intent. Check spec.md acceptance criteria for guidance.`,
            })),
        };
    });
    const contextCount = conflicts.filter((c) => c.linkedSpecs.length > 0).length;
    const conflictLines = conflicts.map((c) => {
        const specInfo = c.linkedSpecs.length > 0 ? ` (linked: ${c.linkedSpecs.map((s) => s.specId).join(', ')})` : '';
        return `${c.file}${specInfo}`;
    });
    const conflictText = [
        `Found ${conflictingFiles.length} conflicting file(s). Context provided for ${contextCount} file(s).`,
        'Resolution decisions are always made by the developer. This tool only provides context.',
        '',
        formatList(conflictLines, 'Conflicting Files'),
    ].join('\n');
    return compactResult(conflictText);
}
function extractSpecFromPath(filePath) {
    const match = /SPEC-\d{3}/.exec(filePath);
    return match?.[0] ?? null;
}
//# sourceMappingURL=sync-ops.js.map