// engine/auditor.ts — Code Auditor Engine (public API + scoring)
// Check functions are in auditor-code-quality.ts, auditor-security.ts, auditor-architecture.ts
import { readFile } from 'node:fs/promises';
import { join, extname } from 'node:path';
import { glob } from 'glob';
import { checkCleanCode, checkDry, checkCustomRule } from './auditor-code-quality.js';
import { checkSecurity, checkErrorHandling, checkPerformance, checkTestingPractices, checkElectronSecurity, } from './auditor-security.js';
import { checkSolid, checkArchitecture } from './auditor-architecture.js';
// === Constants ===
const CODE_EXTENSIONS = new Set([
    '.ts',
    '.tsx',
    '.js',
    '.jsx',
    '.mjs',
    '.cjs',
    '.py',
    '.go',
    '.rs',
    '.java',
    '.kt',
    '.kts',
    '.rb',
    '.php',
    '.cs',
    '.swift',
    '.dart',
    '.ex',
    '.exs',
    '.scala',
    '.clj',
]);
const DEFAULT_IGNORE = [
    'node_modules/**',
    'dist/**',
    'build/**',
    '.git/**',
    'vendor/**',
    '__pycache__/**',
    '.next/**',
    '.nuxt/**',
    'coverage/**',
    '.cache/**',
    '*.min.*',
    '*.bundle.*',
];
// === Public API ===
/**
 * Audit code quality across multiple categories.
 * CHAIN OF RESPONSIBILITY PATTERN: independent check modules chained sequentially.
 * Scans files in the given path and checks against quality rules.
 * See DESIGN_PATTERNS.md § Chain of Responsibility Pattern.
 */
export async function auditCode(path, projectPath, categories = [
    'solid',
    'clean-code',
    'architecture',
    'security',
    'error-handling',
], profile) {
    const effectiveCategories = profile?.enabledCategories ?? categories;
    const customRules = profile?.customRules ?? [];
    const files = await findCodeFiles(path, projectPath);
    const findings = [];
    for (const file of files) {
        const fullPath = join(projectPath, file);
        let content;
        try {
            content = await readFile(fullPath, 'utf-8');
        }
        catch {
            continue;
        }
        const lines = content.split('\n');
        const ext = extname(file);
        if (effectiveCategories.includes('clean-code')) {
            findings.push(...checkCleanCode(file, content, lines));
        }
        if (effectiveCategories.includes('solid')) {
            findings.push(...checkSolid(file, content, lines, ext));
        }
        if (effectiveCategories.includes('architecture')) {
            findings.push(...checkArchitecture(file, content, lines, projectPath));
        }
        if (effectiveCategories.includes('security')) {
            findings.push(...checkSecurity(file, content, lines));
            // SPEC-014b: Electron-specific security checks
            findings.push(...checkElectronSecurity(file, content, lines));
        }
        if (effectiveCategories.includes('error-handling')) {
            findings.push(...checkErrorHandling(file, content, lines, ext));
        }
        if (effectiveCategories.includes('performance')) {
            findings.push(...checkPerformance(file, content, lines, ext));
        }
        if (effectiveCategories.includes('testing')) {
            findings.push(...checkTestingPractices(file, content, lines));
        }
        if (effectiveCategories.includes('dry')) {
            findings.push(...checkDry(file, content, lines));
        }
        for (const rule of customRules) {
            if (effectiveCategories.includes(rule.category)) {
                const match = checkCustomRule(file, content, rule);
                if (match) {
                    findings.push(match);
                }
            }
        }
    }
    const summary = buildSummary(findings, effectiveCategories);
    const score = calculateScore(findings, files.length);
    return {
        filesAnalyzed: files.length,
        findings,
        summary,
        score,
    };
}
/**
 * Audit a single file for quality issues.
 */
export async function auditFile(filePath, categories = ['clean-code', 'security']) {
    let content;
    try {
        content = await readFile(filePath, 'utf-8');
    }
    catch {
        return [];
    }
    const lines = content.split('\n');
    const ext = extname(filePath);
    const findings = [];
    if (categories.includes('clean-code')) {
        findings.push(...checkCleanCode(filePath, content, lines));
    }
    if (categories.includes('security')) {
        findings.push(...checkSecurity(filePath, content, lines));
    }
    if (categories.includes('error-handling')) {
        findings.push(...checkErrorHandling(filePath, content, lines, ext));
    }
    return findings;
}
// === Helpers ===
async function findCodeFiles(path, projectPath) {
    const searchPath = path.startsWith('/') ? path : join(projectPath, path);
    try {
        const files = await glob('**/*', {
            cwd: searchPath,
            nodir: true,
            ignore: DEFAULT_IGNORE,
            maxDepth: 8,
        });
        return files.filter((f) => CODE_EXTENSIONS.has(extname(f))).slice(0, 200);
    }
    catch {
        return [];
    }
}
export function buildSummary(findings, categories) {
    const byCategory = {};
    for (const category of categories) {
        const catFindings = findings.filter((f) => f.category === category);
        const worstSeverity = catFindings.reduce((worst, f) => {
            const severityOrder = { critical: 4, error: 3, warning: 2, info: 1 };
            const fSev = severityOrder[f.severity] ?? 0;
            /* v8 ignore next 1 */
            const wSev = severityOrder[worst] ?? 0;
            return fSev > wSev ? f.severity : worst;
        }, 'info');
        byCategory[category] = {
            count: catFindings.length,
            severity: worstSeverity,
        };
    }
    const ruleCounts = new Map();
    for (const f of findings) {
        ruleCounts.set(f.rule, (ruleCounts.get(f.rule) ?? 0) + 1);
    }
    const topIssues = [...ruleCounts.entries()]
        .sort((a, b) => b[1] - a[1])
        .slice(0, 5)
        .map(([rule, count]) => `${rule}: ${count} occurrences`);
    const strengths = categories
        .filter((cat) => !findings.some((f) => f.category === cat))
        .map((cat) => `No issues found in ${cat}`);
    const score = calculateScore(findings, 0);
    return { byCategory, topIssues, strengths, score };
}
export function calculateScore(findings, fileCount) {
    if (findings.length === 0) {
        return 100;
    }
    const weights = { critical: 10, error: 5, warning: 2, info: 0.5 };
    let penalty = 0;
    for (const finding of findings) {
        penalty += weights[finding.severity] ?? 1;
    }
    const normalizer = Math.max(1, fileCount * 0.5);
    const score = Math.max(0, 100 - (penalty / normalizer) * 10);
    return Math.round(score);
}
//# sourceMappingURL=auditor.js.map