// ---------------------------------------------------------------------------
// Stop words excluded from similarity calculation
// ---------------------------------------------------------------------------
const STOP_WORDS = new Set([
    'the',
    'a',
    'an',
    'is',
    'are',
    'was',
    'were',
    'it',
    'its',
    'in',
    'on',
    'at',
    'to',
    'for',
    'of',
    'and',
    'or',
    'but',
    'not',
    'with',
    'this',
    'that',
    'i',
    'we',
    'my',
    'our',
    'does',
    'do',
    'can',
    'cannot',
    'could',
    'should',
    'when',
    'if',
]);
// ---------------------------------------------------------------------------
// Public: Similarity score (Jaccard word overlap)
// ---------------------------------------------------------------------------
/**
 * Compute a similarity score between two strings using Jaccard index on word sets.
 * Stop words are excluded. Returns a value in [0, 1].
 */
export function similarityScore(a, b) {
    const tokensA = tokenize(a);
    const tokensB = tokenize(b);
    if (tokensA.size === 0 && tokensB.size === 0) {
        return 1;
    }
    if (tokensA.size === 0 || tokensB.size === 0) {
        return 0;
    }
    const intersection = [...tokensA].filter((w) => tokensB.has(w)).length;
    const union = new Set([...tokensA, ...tokensB]).size;
    return union === 0 ? 0 : intersection / union;
}
function tokenize(text) {
    return new Set(text
        .toLowerCase()
        .replace(/[^a-z0-9\s]/g, ' ')
        .split(/\s+/)
        .filter((w) => w.length > 1 && !STOP_WORDS.has(w)));
}
// ---------------------------------------------------------------------------
// Public: Group pending feedback by similarity
// ---------------------------------------------------------------------------
/**
 * Group similar pending feedback entries into triage groups.
 * Two entries are grouped if their title similarity exceeds the threshold (default 0.5).
 */
export function triageFeedback(entries, threshold = 0.5) {
    const pending = entries.filter((e) => e.status === 'pending');
    const visited = new Set();
    const groups = [];
    for (const entry of pending) {
        if (visited.has(entry.id)) {
            continue;
        }
        visited.add(entry.id);
        const similar = [];
        for (const other of pending) {
            if (visited.has(other.id)) {
                continue;
            }
            if (similarityScore(entry.title, other.title) > threshold) {
                similar.push(other);
                visited.add(other.id);
            }
        }
        const totalCount = 1 + similar.length;
        const suggestedAction = determineSuggestedAction(totalCount, entry);
        const suggestedSpecTitle = suggestedAction !== 'dismiss' ? buildSpecTitle(entry, similar) : undefined;
        groups.push({
            representative: entry,
            similar,
            suggestedAction,
            suggestedSpecTitle,
        });
    }
    return groups;
}
function determineSuggestedAction(count, entry) {
    if (count >= 3) {
        return 'create_spec';
    }
    if (count >= 2 || entry.specId) {
        return 'merge_with_existing';
    }
    return 'dismiss';
}
function buildSpecTitle(representative, similar) {
    // Use the most-voted entry's title as the base if votes differ
    const all = [representative, ...similar];
    const topVoted = all.reduce((best, e) => ((e.votes ?? 0) > (best.votes ?? 0) ? e : best), representative);
    return topVoted.title;
}
// ---------------------------------------------------------------------------
// Public: Format triage report as markdown
// ---------------------------------------------------------------------------
const TYPE_EMOJI = {
    bug: 'Bug',
    feature: 'Feature',
    suggestion: 'Suggestion',
    dx: 'DX',
};
/**
 * Format a list of triage groups as a markdown report.
 */
export function formatTriageReport(groups) {
    if (groups.length === 0) {
        return '# Feedback Triage\n\nNo pending feedback to triage.';
    }
    const priority = groups.filter((g) => g.suggestedAction === 'create_spec' || 1 + g.similar.length >= 3);
    const individual = groups.filter((g) => g.suggestedAction !== 'create_spec' && 1 + g.similar.length < 3);
    const lines = ['# Feedback Triage', ''];
    if (priority.length > 0) {
        lines.push('## Priority Groups (3+ similar)', '');
        for (const group of priority) {
            const total = 1 + group.similar.length;
            const ids = [group.representative.id, ...group.similar.map((s) => s.id)].join(', ');
            lines.push(`### "${group.representative.title}" (${total} reports)`);
            lines.push(`- Reports: ${ids}`);
            if (group.suggestedSpecTitle) {
                lines.push(`- Suggested: Create spec — "${group.suggestedSpecTitle}"`);
            }
            lines.push('');
        }
    }
    if (individual.length > 0) {
        lines.push('## Individual Items', '');
        lines.push('| ID | Type | Title | Action |');
        lines.push('|----|------|-------|--------|');
        for (const group of individual) {
            const e = group.representative;
            const typeLabel = TYPE_EMOJI[e.type] ?? e.type;
            lines.push(`| ${e.id} | ${typeLabel} | ${e.title} | ${group.suggestedAction.replace(/_/g, ' ')} |`);
        }
        lines.push('');
    }
    return lines.join('\n');
}
//# sourceMappingURL=triage.js.map