/**
 * Task-to-Changelog generation.
 *
 * Generates CHANGELOG.md content from CLEO task metadata.
 * Groups tasks by labels (features, fixes, docs, etc.) and
 * formats as conventional changelog sections.
 *
 * @task T4454
 * @epic T4454
 */
import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
import { getAccessor } from '../store/data-accessor.js';
/** Label-to-category mapping for changelog sections. */
const LABEL_CATEGORIES = {
    feature: 'Features',
    feat: 'Features',
    enhancement: 'Features',
    bug: 'Bug Fixes',
    fix: 'Bug Fixes',
    bugfix: 'Bug Fixes',
    hotfix: 'Bug Fixes',
    docs: 'Documentation',
    documentation: 'Documentation',
    refactor: 'Refactoring',
    cleanup: 'Refactoring',
    chore: 'Refactoring',
    test: 'Testing',
    testing: 'Testing',
    breaking: 'Breaking Changes',
    'breaking-change': 'Breaking Changes',
};
/**
 * Discover task IDs for a release from completed tasks.
 * Optionally filtered by date range or specific task IDs.
 */
export async function discoverReleaseTasks(options = {}, cwd, accessor) {
    const tasks = [];
    // Collect tasks from both active and archive sources
    const dataSources = [];
    const dataAccessor = accessor ?? (await getAccessor(cwd));
    const { tasks: activeTasks } = await dataAccessor.queryTasks({});
    if (activeTasks?.length)
        dataSources.push({ tasks: activeTasks });
    const archiveData = await dataAccessor.loadArchive();
    if (archiveData?.archivedTasks)
        dataSources.push({ tasks: archiveData.archivedTasks });
    for (const data of dataSources) {
        for (const task of data.tasks) {
            const id = task.id;
            const status = task.status;
            const completedAt = task.completedAt;
            // Only completed tasks
            if (status !== 'done')
                continue;
            // Filter by IDs if specified
            if (options.taskIds?.length && !options.taskIds.includes(id))
                continue;
            // Filter by date range
            if (options.since && completedAt && completedAt < options.since)
                continue;
            if (options.until && completedAt && completedAt > options.until)
                continue;
            tasks.push({
                id,
                title: task.title,
                labels: task.labels ?? [],
                parentId: task.parentId ?? undefined,
                completedAt,
            });
        }
    }
    return tasks;
}
/** Categorize a task based on its labels. Returns the section name. */
function categorizeTask(task) {
    for (const label of task.labels) {
        const category = LABEL_CATEGORIES[label.toLowerCase()];
        if (category)
            return category;
    }
    return 'Other Changes';
}
/** Group tasks into changelog sections. */
export function groupTasksIntoSections(tasks) {
    const groups = new Map();
    for (const task of tasks) {
        const section = categorizeTask(task);
        if (!groups.has(section))
            groups.set(section, []);
        groups.get(section).push({
            taskId: task.id,
            description: task.title,
        });
    }
    // Order: Breaking Changes first, then Features, Bug Fixes, etc.
    const sectionOrder = [
        'Breaking Changes',
        'Features',
        'Bug Fixes',
        'Documentation',
        'Refactoring',
        'Testing',
        'Other Changes',
    ];
    return sectionOrder
        .filter((title) => groups.has(title))
        .map((title) => ({ title, entries: groups.get(title) }));
}
/** Generate changelog markdown for a version. */
export function generateChangelogMarkdown(version, date, sections) {
    const lines = [];
    lines.push(`## [${version}] - ${date}`);
    lines.push('');
    for (const section of sections) {
        lines.push(`### ${section.title}`);
        lines.push('');
        for (const entry of section.entries) {
            lines.push(`- ${entry.description} (${entry.taskId})`);
        }
        lines.push('');
    }
    return lines.join('\n');
}
/** Format changelog data as JSON. */
export function formatChangelogJson(version, date, sections) {
    return {
        version,
        date,
        sections: sections.map((s) => ({
            title: s.title,
            entries: s.entries,
        })),
        totalEntries: sections.reduce((sum, s) => sum + s.entries.length, 0),
    };
}
/** Write changelog content to a file. */
export function writeChangelogFile(filePath, content) {
    writeFileSync(filePath, content, 'utf-8');
}
/** Append a new release section to an existing CHANGELOG.md. */
export function appendToChangelog(filePath, newContent) {
    if (!existsSync(filePath)) {
        writeFileSync(filePath, `# Changelog\n\n${newContent}`, 'utf-8');
        return;
    }
    const existing = readFileSync(filePath, 'utf-8');
    // Insert after the first heading
    const headerMatch = existing.match(/^# .+\n/m);
    if (headerMatch) {
        const insertPos = (headerMatch.index ?? 0) + headerMatch[0].length;
        const updated = existing.slice(0, insertPos) + '\n' + newContent + existing.slice(insertPos);
        writeFileSync(filePath, updated, 'utf-8');
    }
    else {
        appendFileSync(filePath, '\n' + newContent);
    }
}
/**
 * Full changelog generation: discover tasks, group, generate, write.
 */
export async function generateChangelog(version, options = {}, cwd, accessor) {
    const date = new Date().toISOString().split('T')[0];
    const tasks = await discoverReleaseTasks({ since: options.since, until: options.until, taskIds: options.taskIds }, cwd, accessor);
    if (tasks.length === 0) {
        return {
            success: true,
            result: { version, date, sections: [], totalEntries: 0, message: 'No tasks found' },
        };
    }
    const sections = groupTasksIntoSections(tasks);
    const markdown = generateChangelogMarkdown(version, date, sections);
    const outputPath = options.outputPath ?? 'CHANGELOG.md';
    if (options.append) {
        appendToChangelog(outputPath, markdown);
    }
    else {
        writeChangelogFile(outputPath, `# Changelog\n\n${markdown}`);
    }
    return {
        success: true,
        result: formatChangelogJson(version, date, sections),
    };
}
//# sourceMappingURL=changelog.js.map