// engine/diff-spec-generator.ts — Diff-to-Spec generator (SPEC-350)
import { execSync } from 'node:child_process';
// ---------------------------------------------------------------------------
// Diff analysis
// ---------------------------------------------------------------------------
export function analyzeDiff(rawDiff, commitRange) {
    const lines = rawDiff.split('\n');
    const modifiedFiles = [];
    const addedFiles = [];
    const deletedFiles = [];
    const addedFunctions = [];
    const removedFunctions = [];
    const addedEndpoints = [];
    for (const line of lines) {
        // File changes
        if (line.startsWith('+++ b/')) {
            const file = line.slice(6);
            if (!modifiedFiles.includes(file)) {
                modifiedFiles.push(file);
            }
        }
        else if (line.startsWith('new file mode')) {
            // handled below via diff --name-status
        }
        else if (line.startsWith('diff --git a/')) {
            const parts = line.split(' b/');
            if (parts[1] !== undefined && !modifiedFiles.includes(parts[1])) {
                modifiedFiles.push(parts[1]);
            }
        }
        // Added functions (TypeScript/JavaScript/Python)
        const fnMatch = /^\+\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)/.exec(line);
        if (fnMatch?.[1] !== undefined && !addedFunctions.includes(fnMatch[1])) {
            addedFunctions.push(fnMatch[1]);
        }
        // Arrow function / method
        const arrowMatch = /^\+\s*(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/.exec(line);
        if (arrowMatch?.[1] !== undefined && !addedFunctions.includes(arrowMatch[1])) {
            addedFunctions.push(arrowMatch[1]);
        }
        // Removed functions
        const removedFnMatch = /^-\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)/.exec(line);
        if (removedFnMatch?.[1] !== undefined && !removedFunctions.includes(removedFnMatch[1])) {
            removedFunctions.push(removedFnMatch[1]);
        }
        // Endpoints (Express/Fastify/Hono pattern)
        const endpointMatch = /^\+\s*(?:app|router|server)\.(get|post|put|patch|delete|head)\s*\(['"`]([^'"`]+)/.exec(line);
        if (endpointMatch?.[1] !== undefined && endpointMatch[2] !== undefined) {
            const ep = `${endpointMatch[1].toUpperCase()} ${endpointMatch[2]}`;
            if (!addedEndpoints.includes(ep)) {
                addedEndpoints.push(ep);
            }
        }
    }
    return {
        modifiedFiles: [...new Set(modifiedFiles)],
        addedFiles,
        deletedFiles,
        addedFunctions,
        removedFunctions,
        addedEndpoints,
        rawDiff,
        commitRange,
    };
}
// ---------------------------------------------------------------------------
// Spec draft generation
// ---------------------------------------------------------------------------
export function generateSpecDraft(analysis, sourceType, sourceRef, titleOverride) {
    const now = new Date().toISOString();
    const title = titleOverride ?? inferTitle(analysis, sourceRef);
    const description = buildDescription(analysis);
    const acceptanceCriteria = buildCriteria(analysis);
    const estimatedEffort = estimateEffort(analysis);
    return {
        title,
        description,
        acceptanceCriteria,
        estimatedEffort,
        affectedFiles: analysis.modifiedFiles,
        sourceType,
        sourceRef,
        generatedAt: now,
    };
}
// ---------------------------------------------------------------------------
// Git integration helpers
// ---------------------------------------------------------------------------
export function runGitDiff(repoPath, commitRange) {
    const range = commitRange ?? 'HEAD~1..HEAD';
    const cmd = `git -C "${repoPath}" diff ${range}`;
    try {
        return execSync(cmd, { encoding: 'utf-8', timeout: 15_000 });
    }
    catch (err) {
        throw new Error(`Failed to run git diff: ${err instanceof Error ? err.message : String(err)}. ` +
            `Ensure the path is a valid git repository.`, { cause: err });
    }
}
export function runGhPrDiff(repoPath, prNumber) {
    const cmd = `gh pr diff ${prNumber} --repo "${repoPath}"`;
    try {
        return execSync(cmd, { encoding: 'utf-8', timeout: 20_000 });
    }
    catch (err) {
        throw new Error(`Failed to run gh pr diff: ${err instanceof Error ? err.message : String(err)}. ` +
            `Ensure the GitHub CLI (gh) is installed and authenticated. ` +
            `Run: gh auth login`, { cause: err });
    }
}
// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------
function inferTitle(analysis, sourceRef) {
    if (analysis.addedFunctions.length > 0) {
        const names = analysis.addedFunctions.slice(0, 2).join(', ');
        return `Add ${names}`;
    }
    if (analysis.addedEndpoints.length > 0) {
        return `Add endpoints: ${analysis.addedEndpoints.slice(0, 2).join(', ')}`;
    }
    if (analysis.modifiedFiles.length > 0) {
        const first = analysis.modifiedFiles[0] ?? '';
        const base = first.split('/').pop() ?? first;
        return `Update ${base}`;
    }
    return `Changes from ${sourceRef}`;
}
function buildDescription(analysis) {
    const parts = [];
    if (analysis.modifiedFiles.length > 0) {
        parts.push(`Modified ${analysis.modifiedFiles.length} file(s): ${analysis.modifiedFiles.slice(0, 3).join(', ')}${analysis.modifiedFiles.length > 3 ? '...' : ''}.`);
    }
    if (analysis.addedFunctions.length > 0) {
        parts.push(`Added functions: ${analysis.addedFunctions.join(', ')}.`);
    }
    if (analysis.removedFunctions.length > 0) {
        parts.push(`Removed functions: ${analysis.removedFunctions.join(', ')}.`);
    }
    if (analysis.addedEndpoints.length > 0) {
        parts.push(`New endpoints: ${analysis.addedEndpoints.join(', ')}.`);
    }
    return parts.length > 0 ? parts.join(' ') : 'Auto-generated spec from code changes.';
}
function buildCriteria(analysis) {
    const criteria = [];
    for (const fn of analysis.addedFunctions.slice(0, 5)) {
        criteria.push(`${fn} function is implemented and returns expected output`);
    }
    for (const ep of analysis.addedEndpoints.slice(0, 5)) {
        criteria.push(`${ep} endpoint responds with correct status code`);
    }
    if (analysis.modifiedFiles.length > 0) {
        criteria.push('All modified files compile without errors');
        criteria.push('Existing tests continue to pass');
    }
    if (criteria.length === 0) {
        criteria.push('Code changes are covered by unit tests');
        criteria.push('No regressions introduced');
    }
    return criteria;
}
function estimateEffort(analysis) {
    const signals = analysis.modifiedFiles.length +
        analysis.addedFunctions.length * 2 +
        analysis.addedEndpoints.length * 3;
    if (signals < 5) {
        return 'XS (< 1h)';
    }
    if (signals < 15) {
        return 'S (1-4h)';
    }
    if (signals < 35) {
        return 'M (4-8h)';
    }
    if (signals < 70) {
        return 'L (1-2d)';
    }
    return 'XL (> 2d)';
}
//# sourceMappingURL=diff-spec-generator.js.map