// engine/convention-scanner/convention-parser.ts — SPEC-192
// Parses CLAUDE.md and .claude/rules/*.md to extract ConventionRule[]
import { readFileSync, readdirSync } from 'node:fs';
import { join, basename } from 'node:path';
// ── keyword maps ──────────────────────────────────────────────────────────────
const NAMING_KEYWORDS = [
    'english',
    'camelcase',
    'pascalcase',
    'kebab-case',
    'snake_case',
    'screaming_snake',
    'naming convention',
    'variable names',
    'identifiers',
];
const I18N_KEYWORDS = [
    'i18n',
    'hardcoded string',
    'no hardcoded',
    'internationalization',
    'localization',
    'translation',
    'locale',
];
const FILE_LIMIT_KEYWORDS = [
    'max lines',
    'file size',
    'max-lines',
    'line limit',
    'lines per file',
    'lines per function',
    'max-lines-per-function',
];
const ARCHITECTURE_KEYWORDS = [
    'import',
    'no-restricted-imports',
    'dependency rule',
    'circular',
    'dependency',
    'layer',
    'cross-layer',
    'barrel export',
];
const TESTING_KEYWORDS = [
    'test',
    'coverage',
    'tdd',
    'test-driven',
    'vitest',
    'jest',
    'spec file',
    'test file',
    'coverage threshold',
];
const CODE_QUALITY_KEYWORDS = [
    'complexity',
    'solid',
    'srp',
    'ocp',
    'eslint',
    'lint',
    'prettier',
    'typescript strict',
    'strict mode',
    'any type',
    'type assertion',
];
// ── helpers ───────────────────────────────────────────────────────────────────
function readFileSafe(path) {
    try {
        return readFileSync(path, 'utf-8');
    }
    catch {
        return '';
    }
}
function listRulesFiles(projectPath) {
    const rulesDir = join(projectPath, '.claude', 'rules');
    try {
        return readdirSync(rulesDir)
            .filter((f) => f.endsWith('.md'))
            .map((f) => join(rulesDir, f));
    }
    catch {
        return [];
    }
}
function matchesKeywords(line, keywords) {
    const lower = line.toLowerCase();
    return keywords.some((kw) => lower.includes(kw));
}
function categorize(line) {
    if (matchesKeywords(line, NAMING_KEYWORDS)) {
        return 'naming';
    }
    if (matchesKeywords(line, I18N_KEYWORDS)) {
        return 'i18n';
    }
    if (matchesKeywords(line, FILE_LIMIT_KEYWORDS)) {
        return 'file-limits';
    }
    if (matchesKeywords(line, ARCHITECTURE_KEYWORDS)) {
        return 'architecture';
    }
    if (matchesKeywords(line, TESTING_KEYWORDS)) {
        return 'testing';
    }
    if (matchesKeywords(line, CODE_QUALITY_KEYWORDS)) {
        return 'code-quality';
    }
    return null;
}
function inferSeverity(line) {
    const lower = line.toLowerCase();
    if (lower.includes('mandatory') || lower.includes('never') || lower.includes('always')) {
        return 'critical';
    }
    if (lower.includes('should') || lower.includes('prefer') || lower.includes('avoid')) {
        return 'warning';
    }
    return 'info';
}
function extractDescription(line) {
    // Strip markdown markers and trim
    return line
        .replace(/^#+\s*/, '')
        .replace(/^\*+\s*/, '')
        .replace(/^-+\s*/, '')
        .replace(/`/g, '')
        .replace(/\*\*/g, '')
        .trim()
        .slice(0, 120);
}
function parseContent(content, sourceFile, source, idPrefix) {
    const rules = [];
    const seen = new Set();
    let counter = 0;
    const lines = content.split('\n');
    for (const raw of lines) {
        const line = raw.trim();
        if (!line || line.length < 10) {
            continue;
        }
        const category = categorize(line);
        if (!category) {
            continue;
        }
        const description = extractDescription(line);
        // Deduplicate by normalized description
        const key = `${category}:${description.toLowerCase().slice(0, 60)}`;
        if (seen.has(key)) {
            continue;
        }
        seen.add(key);
        counter++;
        rules.push({
            id: `${idPrefix}-${category}-${counter}`,
            category,
            description,
            severity: inferSeverity(line),
            source,
            sourceFile,
        });
    }
    return rules;
}
// ── public API ────────────────────────────────────────────────────────────────
/**
 * Parses CLAUDE.md and .claude/rules/*.md files to produce a structured
 * list of ConventionRule objects. Never throws — returns empty array on error.
 */
export function parseConventions(projectPath) {
    const rules = [];
    // Parse CLAUDE.md
    const claudeMdPath = join(projectPath, 'CLAUDE.md');
    const claudeContent = readFileSafe(claudeMdPath);
    if (claudeContent) {
        const claudeRules = parseContent(claudeContent, 'CLAUDE.md', 'claude-md', 'claude-md');
        rules.push(...claudeRules);
    }
    // Parse .claude/rules/*.md
    const rulesFiles = listRulesFiles(projectPath);
    for (const filePath of rulesFiles) {
        const content = readFileSafe(filePath);
        if (!content) {
            continue;
        }
        const name = basename(filePath, '.md');
        const fileRules = parseContent(content, filePath, 'rules-file', `rules-${name}`);
        rules.push(...fileRules);
    }
    return rules;
}
//# sourceMappingURL=convention-parser.js.map