// engine/github/changelog.ts — Generate changelog from merged PRs.
import { ghJson } from './gh-cli.js';
const TYPE_PREFIXES = {
    feat: 'feat',
    fix: 'fix',
    docs: 'docs',
    refactor: 'refactor',
    perf: 'perf',
    test: 'test',
    chore: 'chore',
};
/**
 * Parse a conventional commit title to extract the type.
 */
export function parseCommitType(title) {
    const match = /^(\w+)[\s(!:(]/.exec(title);
    const prefix = match?.[1]?.toLowerCase();
    const mapped = prefix ? TYPE_PREFIXES[prefix] : undefined;
    if (mapped) {
        return mapped;
    }
    return 'other';
}
/**
 * Extract issue numbers referenced in a PR body (e.g., "Closes #123", "Fixes #456").
 */
export function extractIssueRefs(body) {
    const pattern = /(?:closes?|fixes?|resolves?)\s+#(\d+)/gi;
    const issues = [];
    let match;
    while ((match = pattern.exec(body)) !== null) {
        const num = match[1];
        if (num) {
            issues.push(Number(num));
        }
    }
    return issues;
}
/**
 * Fetch merged PRs between two git refs using gh CLI.
 */
export async function fetchMergedPRs(since, _until, exec) {
    const args = [
        'pr',
        'list',
        '--state',
        'merged',
        '--search',
        `merged:>=${since}`,
        '--json',
        'number,title,url,author,body',
        '--limit',
        '100',
    ];
    const prs = await ghJson(args, exec);
    return prs.map((pr) => ({
        type: parseCommitType(pr.title),
        title: pr.title,
        prNumber: pr.number,
        prUrl: pr.url,
        author: pr.author.login,
        issues: extractIssueRefs(pr.body),
    }));
}
/**
 * Group changelog entries by type.
 */
export function groupByType(entries) {
    const grouped = {};
    for (const entry of entries) {
        const group = grouped[entry.type] ?? [];
        group.push(entry);
        grouped[entry.type] = group;
    }
    return grouped;
}
const TYPE_LABELS = {
    feat: 'Features',
    fix: 'Bug Fixes',
    docs: 'Documentation',
    refactor: 'Refactoring',
    perf: 'Performance',
    test: 'Tests',
    chore: 'Chores',
    other: 'Other Changes',
};
/**
 * Format changelog entries as Keep a Changelog markdown.
 */
export function formatChangelog(entries, since, until) {
    const grouped = groupByType(entries);
    const sections = [];
    const date = new Date().toISOString().split('T')[0] ?? 'unknown';
    sections.push(`# Changelog\n`);
    sections.push(`## [Unreleased] — ${date}\n`);
    sections.push(`Changes from ${since} to ${until}.\n`);
    for (const type of Object.keys(TYPE_LABELS)) {
        const group = grouped[type];
        if (!group || group.length === 0) {
            continue;
        }
        sections.push(`### ${TYPE_LABELS[type]}\n`);
        for (const entry of group) {
            const issues = entry.issues.length > 0 ? ` (${entry.issues.map((i) => `#${String(i)}`).join(', ')})` : '';
            sections.push(`- ${entry.title} ([#${String(entry.prNumber)}](${entry.prUrl}))${issues}`);
        }
        sections.push('');
    }
    return sections.join('\n');
}
/**
 * Format changelog entries as JSON.
 */
export function formatChangelogJson(entries) {
    return JSON.stringify({ entries, generatedAt: new Date().toISOString() }, null, 2);
}
//# sourceMappingURL=changelog.js.map