// ---------------------------------------------------------------------------
// Filtering
// ---------------------------------------------------------------------------
/**
 * Filter specs by status=done and updatedAt within [fromDate, toDate].
 */
export function filterSpecsByDateRange(specs, fromDate, toDate) {
    const from = new Date(fromDate).getTime();
    const to = new Date(toDate).getTime();
    return specs.filter((spec) => {
        if (spec.status !== 'done') {
            return false;
        }
        const dateStr = spec.updatedAt;
        if (!dateStr) {
            return false;
        }
        const ts = new Date(dateStr).getTime();
        return ts >= from && ts <= to;
    });
}
// ---------------------------------------------------------------------------
// Entry mapping
// ---------------------------------------------------------------------------
const BREAKING_KEYWORDS = ['breaking', 'remove', 'deprecat'];
const BREAKING_TAG = 'breaking-change';
function detectBreakingChange(spec) {
    const titleLower = spec.title.toLowerCase();
    const hasKeyword = BREAKING_KEYWORDS.some((kw) => titleLower.includes(kw));
    const hasTag = spec.tags.includes(BREAKING_TAG);
    return hasKeyword || hasTag;
}
function buildSummary(spec) {
    // Derive a user-facing 1-2 sentence summary from the title and scope/type context
    const typeLabel = spec.type === 'bugfix'
        ? 'Fixed'
        : spec.type === 'refactor'
            ? 'Improved'
            : spec.type === 'infra'
                ? 'Updated infrastructure for'
                : 'Added';
    return `${typeLabel} ${spec.title.charAt(0).toLowerCase()}${spec.title.slice(1)}.`;
}
function priorityLabel(spec) {
    const risk = spec.risk;
    if (risk === 'critical') {
        return 'critical';
    }
    if (risk === 'high') {
        return 'high';
    }
    if (risk === 'medium') {
        return 'medium';
    }
    return 'low';
}
/**
 * Convert a Spec to a ReleaseNotesEntry.
 */
export function specToReleaseEntry(spec) {
    return {
        specId: spec.id,
        title: spec.title,
        summary: buildSummary(spec),
        tags: [...spec.tags],
        priority: priorityLabel(spec),
        completedAt: spec.updatedAt,
        isBreakingChange: detectBreakingChange(spec),
    };
}
// ---------------------------------------------------------------------------
// Grouping
// ---------------------------------------------------------------------------
const RELEVANT_TAGS = ['feature', 'bugfix', 'security', 'performance'];
function primaryGroup(entry) {
    for (const tag of RELEVANT_TAGS) {
        if (entry.tags.includes(tag)) {
            return tag;
        }
    }
    // Fallback: derive from spec type embedded in tags or default 'other'
    return 'other';
}
/**
 * Group entries by their primary relevant tag.
 * Groups: feature, bugfix, security, performance, other.
 */
export function groupEntriesByTag(entries) {
    const groups = new Map();
    for (const entry of entries) {
        const group = primaryGroup(entry);
        const existing = groups.get(group);
        if (existing !== undefined) {
            existing.push(entry);
        }
        else {
            groups.set(group, [entry]);
        }
    }
    return groups;
}
// ---------------------------------------------------------------------------
// Rendering
// ---------------------------------------------------------------------------
function sectionHeader(label) {
    return `### ${label}\n`;
}
function renderEntry(entry, audience) {
    const breakingMark = entry.isBreakingChange ? ' ⚠️' : '';
    if (audience === 'developer') {
        const tagStr = entry.tags.length > 0 ? ` \`[${entry.tags.join(', ')}]\`` : '';
        return `- **[${entry.specId}]**${breakingMark} ${entry.title}${tagStr} — ${entry.summary}`;
    }
    if (audience === 'stakeholder') {
        return `- ${entry.title}${breakingMark}: ${entry.summary} *(${entry.priority} priority)*`;
    }
    // user audience — simple language, no technical IDs
    return `- ${entry.title}${breakingMark}: ${entry.summary}`;
}
function buildHeader(version, date) {
    const versionStr = version ? ` ${version}` : '';
    return `# Release Notes${versionStr}\n\n*Generated ${date}*\n`;
}
/**
 * Render entries to Markdown format.
 */
export function renderMarkdown(entries, audience, version, groupByTag) {
    const now = new Date().toISOString().slice(0, 10);
    const parts = [buildHeader(version, now), "## What's New\n"];
    if (entries.length === 0) {
        parts.push('*No completed specs found in this date range.*\n');
        return parts.join('\n');
    }
    const breakingEntries = entries.filter((e) => e.isBreakingChange);
    const regularEntries = entries.filter((e) => !e.isBreakingChange);
    if (groupByTag === true) {
        const groups = groupEntriesByTag(regularEntries);
        const sectionLabels = {
            feature: 'Features',
            bugfix: 'Bug Fixes',
            security: 'Security',
            performance: 'Performance',
            other: 'Other Changes',
        };
        for (const [tag, label] of Object.entries(sectionLabels)) {
            const group = groups.get(tag);
            if (group !== undefined && group.length > 0) {
                parts.push(sectionHeader(label));
                for (const entry of group) {
                    parts.push(renderEntry(entry, audience));
                }
                parts.push('');
            }
        }
    }
    else {
        parts.push(sectionHeader('Changes'));
        for (const entry of regularEntries) {
            parts.push(renderEntry(entry, audience));
        }
        parts.push('');
    }
    if (breakingEntries.length > 0) {
        parts.push(sectionHeader('Breaking Changes ⚠️'));
        for (const entry of breakingEntries) {
            parts.push(renderEntry(entry, audience));
        }
        parts.push('');
    }
    return parts.join('\n');
}
/**
 * Render entries to simple HTML with inline styles.
 */
export function renderHtml(entries, audience, version) {
    const versionStr = version ? ` ${version}` : '';
    const title = `Release Notes${versionStr}`;
    const entryItems = entries
        .map((e) => {
        const breaking = e.isBreakingChange
            ? ' <span style="color:#e74c3c;font-weight:bold">⚠️ Breaking</span>'
            : '';
        const idPart = audience === 'developer' ? ` <code>[${e.specId}]</code>` : '';
        return `  <li>${idPart}<strong>${e.title}</strong>${breaking}: ${e.summary}</li>`;
    })
        .join('\n');
    return [
        '<!DOCTYPE html>',
        '<html lang="en">',
        '<head>',
        `  <meta charset="UTF-8">`,
        `  <title>${title}</title>`,
        '  <style>body{font-family:sans-serif;max-width:800px;margin:2rem auto;padding:0 1rem}',
        '  h1{color:#2c3e50}li{margin:.4rem 0}</style>',
        '</head>',
        '<body>',
        `  <h1>${title}</h1>`,
        '  <ul>',
        entryItems,
        '  </ul>',
        '</body>',
        '</html>',
    ].join('\n');
}
/**
 * Render entries to plain text.
 */
export function renderPlain(entries, audience, version) {
    const versionStr = version ? ` ${version}` : '';
    const lines = [`Release Notes${versionStr}`, ''];
    for (const entry of entries) {
        const breaking = entry.isBreakingChange ? ' [BREAKING]' : '';
        if (audience === 'developer') {
            lines.push(`[${entry.specId}]${breaking} ${entry.title}`);
            lines.push(`  ${entry.summary}`);
        }
        else {
            lines.push(`${entry.title}${breaking}`);
            lines.push(`  ${entry.summary}`);
        }
    }
    return lines.join('\n');
}
// ---------------------------------------------------------------------------
// Orchestrator
// ---------------------------------------------------------------------------
/**
 * Orchestrate the full release notes generation pipeline.
 */
export function generateReleaseNotes(input, specs) {
    const now = new Date();
    const toDate = input.toDate ?? now.toISOString();
    const fromDate = input.fromDate ?? new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString();
    const audience = input.audience ?? 'user';
    const format = input.format ?? 'markdown';
    const maxEntries = input.maxEntries ?? 50;
    const filtered = filterSpecsByDateRange(specs, fromDate, toDate).slice(0, maxEntries);
    const entries = filtered.map(specToReleaseEntry);
    let rendered;
    if (format === 'html') {
        rendered = renderHtml(entries, audience, input.version);
    }
    else if (format === 'plain') {
        rendered = renderPlain(entries, audience, input.version);
    }
    else {
        rendered = renderMarkdown(entries, audience, input.version);
    }
    return {
        version: input.version,
        dateRange: { from: fromDate, to: toDate },
        audience,
        format,
        entries,
        markdown: rendered,
        generatedAt: now.toISOString(),
    };
}
//# sourceMappingURL=release-notes-generator.js.map