// engine/housekeeping/orphan-brainstorming-cleaner.ts — SPEC-755
// Scans docs/superpowers/specs/*.md for orphan brainstorming markdown files.
// When a matching Planu spec exists → deletes the markdown.
// When no match → imports via create-spec path then deletes the markdown.
import { readdir, readFile, unlink } from 'node:fs/promises';
import { join, basename } from 'node:path';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/**
 * Normalize a title or filename to a comparable key (lowercase, alpha-numeric only).
 */
function normalizeTitle(input) {
    return input
        .toLowerCase()
        .replace(/[-_./\\]+/g, ' ')
        .replace(/[^a-z0-9 ]/g, '')
        .replace(/\s+/g, ' ')
        .trim();
}
/**
 * Fuzzy match: return true if the normalized forms share enough tokens.
 */
function titlesAreSimilar(a, b) {
    const na = normalizeTitle(a);
    const nb = normalizeTitle(b);
    if (na === nb) {
        return true;
    }
    if (na.includes(nb) || nb.includes(na)) {
        return true;
    }
    const wordsA = new Set(na.split(' ').filter(Boolean));
    const wordsB = new Set(nb.split(' ').filter(Boolean));
    /* c8 ignore next 3 */
    if (wordsA.size === 0 || wordsB.size === 0) {
        return false;
    }
    let shared = 0;
    for (const w of wordsA) {
        if (wordsB.has(w)) {
            shared++;
        }
    }
    return shared / Math.min(wordsA.size, wordsB.size) >= 0.5;
}
/**
 * Extract a title from the first H1 or frontmatter `title:` field of a markdown file.
 */
function extractTitle(raw) {
    const fmMatch = /^---[\s\S]*?^title:\s*["']?([^"'\n]+)["']?/m.exec(raw);
    if (fmMatch?.[1]) {
        return fmMatch[1].trim();
    }
    const h1Match = /^#\s+(.+)/m.exec(raw);
    if (h1Match?.[1]) {
        return h1Match[1].trim();
    }
    return '';
}
async function readMarkdownTitle(filePath, fallback) {
    try {
        const raw = await readFile(filePath, 'utf-8');
        return extractTitle(raw) || fallback;
    }
    catch {
        return null;
    }
}
async function processMatchedFile(filePath, file, specId, dryRun) {
    if (!dryRun) {
        try {
            await unlink(filePath);
        }
        catch {
            return { file, action: 'skipped' };
        }
    }
    return { file, action: 'deleted_matched', matchedSpecId: specId };
}
async function processUnmatchedFile(filePath, file, markdownTitle, opts) {
    const { dryRun, projectId, createSpec } = opts;
    if (!createSpec) {
        return { file, action: 'skipped' };
    }
    if (dryRun) {
        return { file, action: 'imported_then_deleted' };
    }
    let newSpecId;
    try {
        const description = await readFile(filePath, 'utf-8');
        const created = await createSpec({ projectId, title: markdownTitle, description });
        newSpecId = created.id;
        await unlink(filePath);
    }
    catch {
        return { file, action: 'skipped' };
    }
    return { file, action: 'imported_then_deleted', matchedSpecId: newSpecId };
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Scan docs/superpowers/specs/*.md and reconcile with Planu specs.
 */
export async function cleanOrphanBrainstormingMarkdowns(opts) {
    const { projectPath, projectId, dryRun, listSpecs } = opts;
    const scanDir = join(projectPath, 'docs', 'superpowers', 'specs');
    const result = { scanned: 0, entries: [] };
    let files;
    try {
        files = (await readdir(scanDir)).filter((f) => f.endsWith('.md'));
    }
    catch {
        return result;
    }
    if (files.length === 0) {
        return result;
    }
    result.scanned = files.length;
    let planuSpecs;
    try {
        planuSpecs = await listSpecs(projectId);
    }
    catch {
        planuSpecs = [];
    }
    for (const file of files) {
        const filePath = join(scanDir, file);
        const stemTitle = basename(file, '.md').replace(/[-_]+/g, ' ');
        const markdownTitle = await readMarkdownTitle(filePath, stemTitle);
        if (markdownTitle === null) {
            result.entries.push({ file, action: 'skipped' });
            continue;
        }
        const matched = planuSpecs.find((s) => titlesAreSimilar(s.title, markdownTitle));
        const entry = matched
            ? await processMatchedFile(filePath, file, matched.id, dryRun)
            : await processUnmatchedFile(filePath, file, markdownTitle, opts);
        result.entries.push(entry);
    }
    return result;
}
//# sourceMappingURL=orphan-brainstorming-cleaner.js.map