// engine/global-rules-manager.ts — Global Steering Rules manager (SPEC-351)
import { readFile, writeFile, readdir, unlink, mkdir } from 'node:fs/promises';
import { homedir } from 'node:os';
import { join, dirname } from 'node:path';
// ---------------------------------------------------------------------------
// Paths
// ---------------------------------------------------------------------------
export function globalRulesDir() {
    return join(homedir(), '.planu', 'global-rules');
}
function ruleFile(slug) {
    return join(globalRulesDir(), `${slug}.md`);
}
// ---------------------------------------------------------------------------
// Frontmatter helpers
// ---------------------------------------------------------------------------
function buildFrontmatter(rule) {
    const tagsStr = rule.tags.map((t) => `  - ${t}`).join('\n');
    const authorLine = rule.author !== undefined ? `author: "${rule.author}"\n` : '';
    return [
        '---',
        `name: "${rule.name}"`,
        `tags:`,
        tagsStr,
        `version: "${rule.version}"`,
        authorLine.trim(),
        `createdAt: "${rule.createdAt}"`,
        `updatedAt: "${rule.updatedAt}"`,
        '---',
        '',
        rule.content,
    ].join('\n');
}
function parseFrontmatter(raw, slug) {
    const match = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/m.exec(raw);
    if (match === null) {
        return {
            slug,
            name: slug,
            content: raw,
            tags: [],
            version: '1.0.0',
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString(),
        };
    }
    const fm = match[1] ?? '';
    const content = (match[2] ?? '').trim();
    const nameMatch = /^name:\s*"?([^"\n]+)"?/m.exec(fm);
    const versionMatch = /^version:\s*"?([^"\n]+)"?/m.exec(fm);
    const authorMatch = /^author:\s*"?([^"\n]+)"?/m.exec(fm);
    const createdMatch = /^createdAt:\s*"?([^"\n]+)"?/m.exec(fm);
    const updatedMatch = /^updatedAt:\s*"?([^"\n]+)"?/m.exec(fm);
    const tagMatches = [...fm.matchAll(/^\s{2}-\s+(.+)$/gm)].map((m) => m[1]?.trim() ?? '');
    return {
        slug,
        name: nameMatch?.[1]?.trim() ?? slug,
        content,
        tags: tagMatches,
        version: versionMatch?.[1]?.trim() ?? '1.0.0',
        author: authorMatch?.[1]?.trim(),
        createdAt: createdMatch?.[1]?.trim() ?? new Date().toISOString(),
        updatedAt: updatedMatch?.[1]?.trim() ?? new Date().toISOString(),
    };
}
// ---------------------------------------------------------------------------
// CRUD operations
// ---------------------------------------------------------------------------
async function ensureRulesDir() {
    await mkdir(globalRulesDir(), { recursive: true });
}
export async function createRule(slug, name, content, tags, author, version = '1.0.0') {
    await ensureRulesDir();
    const now = new Date().toISOString();
    const rule = {
        slug,
        name,
        content,
        tags,
        version,
        author,
        createdAt: now,
        updatedAt: now,
    };
    await writeFile(ruleFile(slug), buildFrontmatter(rule), 'utf-8');
    return rule;
}
export async function getRule(slug) {
    try {
        const raw = await readFile(ruleFile(slug), 'utf-8');
        return parseFrontmatter(raw, slug);
    }
    catch (err) {
        if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
            return null;
        }
        /* v8 ignore next */
        throw err;
    }
}
export async function updateRule(slug, updates) {
    const existing = await getRule(slug);
    if (existing === null) {
        return null;
    }
    const updated = {
        ...existing,
        ...updates,
        updatedAt: new Date().toISOString(),
    };
    await writeFile(ruleFile(slug), buildFrontmatter(updated), 'utf-8');
    return updated;
}
export async function deleteRule(slug) {
    try {
        await unlink(ruleFile(slug));
        return true;
    }
    catch (err) {
        if (err instanceof Error && 'code' in err && err.code === 'ENOENT') {
            return false;
        }
        /* v8 ignore next */
        throw err;
    }
}
export async function listRules() {
    await ensureRulesDir();
    let entries;
    try {
        entries = await readdir(globalRulesDir());
    }
    catch {
        /* v8 ignore next */
        return { totalRules: 0, rules: [] };
    }
    const mdFiles = entries.filter((e) => e.endsWith('.md'));
    const rules = await Promise.all(mdFiles.map(async (file) => {
        const slug = file.replace(/\.md$/, '');
        const rule = await getRule(slug);
        return rule;
    }));
    const valid = rules.filter((r) => r !== null);
    return {
        totalRules: valid.length,
        rules: valid.map(({ slug, name, tags, version, author, createdAt, updatedAt }) => ({
            slug,
            name,
            tags,
            version,
            author,
            createdAt,
            updatedAt,
        })),
    };
}
// ---------------------------------------------------------------------------
// Apply rule to project CLAUDE.md
// ---------------------------------------------------------------------------
export async function applyRule(slug, projectPath) {
    const rule = await getRule(slug);
    if (rule === null) {
        throw new Error(`Rule "${slug}" not found. Use manage_global_rules with action "list" to see available rules.`);
    }
    const claudeMdPath = join(projectPath, 'CLAUDE.md');
    let existing = '';
    try {
        existing = await readFile(claudeMdPath, 'utf-8');
    }
    catch {
        // File doesn't exist yet — will create it
    }
    const sectionHeader = `## Applied Global Rules\n### ${rule.name}\n`;
    const section = `\n\n${sectionHeader}${rule.content}`;
    let updated;
    if (existing.includes(`### ${rule.name}`)) {
        // Replace existing section
        const pattern = new RegExp(`### ${escapeRegex(rule.name)}[\\s\\S]*?(?=\n### |$)`, 'g');
        updated = existing.replace(pattern, `### ${rule.name}\n${rule.content}`);
    }
    else if (existing.includes('## Applied Global Rules')) {
        // Append to existing Applied section
        updated = existing + `\n### ${rule.name}\n${rule.content}`;
    }
    else {
        updated = existing + section;
    }
    await mkdir(dirname(claudeMdPath), { recursive: true });
    await writeFile(claudeMdPath, updated, 'utf-8');
    return {
        slug,
        name: rule.name,
        appliedTo: claudeMdPath,
        section: sectionHeader,
        appliedAt: new Date().toISOString(),
    };
}
// ---------------------------------------------------------------------------
// Export
// ---------------------------------------------------------------------------
export async function exportRule(slug) {
    const rule = await getRule(slug);
    if (rule === null) {
        throw new Error(`Rule "${slug}" not found.`);
    }
    return buildFrontmatter(rule);
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function escapeRegex(str) {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
//# sourceMappingURL=global-rules-manager.js.map