// engine/spec-differ.ts — Facade for spec version diff and history (SPEC-284)
import { diffVersions, listVersions, getLatestVersionTag, computeLineDiff, } from './spec-versioning/index.js';
/** Format a SpecVersionDiff as a human-readable +/- text block. */
export function formatDiffAsText(diff) {
    const lines = [];
    lines.push(`--- ${diff.specId} @ ${diff.fromTag}`);
    lines.push(`+++ ${diff.specId} @ ${diff.toTag}`);
    lines.push('');
    if (diff.removed.length === 0 && diff.added.length === 0) {
        lines.push('(no line changes)');
    }
    for (const line of diff.removed) {
        lines.push(`- ${line}`);
    }
    for (const line of diff.added) {
        lines.push(`+ ${line}`);
    }
    lines.push('');
    lines.push('## Criteria changes');
    lines.push(`  Added:    ${diff.criteriaChanges.added.length}`);
    lines.push(`  Removed:  ${diff.criteriaChanges.removed.length}`);
    lines.push(`  Modified: ${diff.criteriaChanges.modified.length}`);
    if (diff.sectionChanges.length > 0) {
        lines.push('');
        lines.push('## Section changes');
        for (const change of diff.sectionChanges) {
            lines.push(`  ${change}`);
        }
    }
    return lines.join('\n');
}
/** Build a summary string for a version relative to its predecessor. */
function buildChangeSummary(currentContent, prevContent) {
    if (prevContent === null) {
        return 'initial version';
    }
    const { added, removed } = computeLineDiff(prevContent, currentContent);
    const parts = [];
    if (added.length > 0) {
        parts.push(`+${added.length} lines`);
    }
    if (removed.length > 0) {
        parts.push(`-${removed.length} lines`);
    }
    return parts.length > 0 ? parts.join(', ') : 'no line changes';
}
/**
 * Compute a diff between two version tags of a spec.
 * If toVersion is omitted, uses the latest version.
 * If both fromVersion and toVersion are omitted, compares latest vs penultimate.
 */
export async function computeSpecVersionDiff(opts) {
    const { dataDir, specId } = opts;
    let fromTag = opts.fromVersion;
    let toTag = opts.toVersion;
    if (fromTag === undefined || toTag === undefined) {
        const versions = await listVersions(dataDir, specId);
        if (versions.length < 2) {
            throw new Error(`Spec "${specId}" has fewer than 2 versions — cannot compute diff. ` +
                `Create more version snapshots with version_spec first.`);
        }
        const latest = versions[versions.length - 1];
        const penultimate = versions[versions.length - 2];
        // Guaranteed to exist — we checked length >= 2 above
        /* v8 ignore start */
        if (latest === undefined || penultimate === undefined) {
            throw new Error(`Unexpected: versions array out of bounds for spec "${specId}".`);
        }
        /* v8 ignore stop */
        toTag ??= latest.tag;
        fromTag ??= penultimate.tag;
    }
    return diffVersions(dataDir, specId, fromTag, toTag);
}
/**
 * List all version snapshots for a spec as VersionSummary objects.
 * Returns them in reverse chronological order (newest first).
 */
export async function listSpecVersionHistory(dataDir, specId, limit = 20) {
    const versions = await listVersions(dataDir, specId);
    if (versions.length === 0) {
        return [];
    }
    const latestTag = await getLatestVersionTag(dataDir, specId);
    const summaries = [];
    // Build summaries newest-first
    const reversed = [...versions].reverse();
    for (let i = 0; i < Math.min(reversed.length, limit); i++) {
        const version = reversed[i];
        /* v8 ignore start */
        if (version === undefined) {
            continue;
        }
        /* v8 ignore stop */
        const prevInOriginal = versions[versions.length - 1 - i - 1];
        const prevContent = prevInOriginal?.content ?? null;
        summaries.push({
            tag: version.tag,
            timestamp: version.timestamp,
            ...(version.author !== undefined ? { author: version.author } : {}),
            ...(version.message !== undefined ? { message: version.message } : {}),
            changeSummary: buildChangeSummary(version.content, prevContent),
        });
    }
    // Mark the latest version explicitly in the first summary
    if (summaries[0]?.tag === latestTag) {
        const first = summaries[0];
        summaries[0] = {
            ...first,
            changeSummary: `[latest] ${first.changeSummary}`,
        };
    }
    return summaries;
}
//# sourceMappingURL=spec-differ.js.map