// engine/auditor-code-quality.ts — Clean Code, DRY, Custom Rule checks
// Extracted from auditor.ts to stay within 400-line limit.
// === Clean Code Checks ===
const MAX_FILE_LENGTH = 500;
const MAX_FUNCTION_LENGTH = 50;
const MAX_PARAMS = 4;
export function checkCleanCode(file, content, lines) {
    const findings = [];
    // File length
    if (lines.length > MAX_FILE_LENGTH) {
        findings.push({
            file,
            category: 'clean-code',
            severity: 'warning',
            rule: 'file-too-long',
            message: `File has ${lines.length} lines (max recommended: ${MAX_FILE_LENGTH})`,
            suggestion: 'Split into smaller, focused modules following Single Responsibility Principle.',
        });
    }
    // Long functions detection (approximate via indentation-based block scanning)
    const functionStarts = findFunctionBoundaries(lines);
    for (const fn of functionStarts) {
        if (fn.length > MAX_FUNCTION_LENGTH) {
            findings.push({
                file,
                line: fn.startLine,
                category: 'clean-code',
                severity: 'warning',
                rule: 'function-too-long',
                message: `Function "${fn.name}" is ${fn.length} lines (max recommended: ${MAX_FUNCTION_LENGTH})`,
                suggestion: 'Extract sub-operations into helper functions. Each function should do one thing.',
            });
        }
    }
    // Too many parameters
    const paramMatches = content.matchAll(/(?:function|def|func|fn)\s+\w+\s*\(([^)]{80,})\)/g);
    for (const match of paramMatches) {
        if (match[1]) {
            const paramCount = match[1].split(',').length;
            if (paramCount > MAX_PARAMS) {
                findings.push({
                    file,
                    category: 'clean-code',
                    severity: 'warning',
                    rule: 'too-many-params',
                    message: `Function has ${paramCount} parameters (max recommended: ${MAX_PARAMS})`,
                    suggestion: 'Group related parameters into an options/config object.',
                });
            }
        }
    }
    // TODO/FIXME/HACK markers
    for (let i = 0; i < lines.length; i++) {
        const currentLine = lines[i];
        if (currentLine === undefined) {
            continue;
        }
        const match = /\b(TODO|FIXME|HACK|XXX)\b:?\s*(.*)/.exec(currentLine);
        if (match) {
            findings.push({
                file,
                line: i + 1,
                category: 'clean-code',
                severity: 'info',
                rule: 'pending-marker',
                message: `${match[1]}: ${match[2]?.trim() ?? '(no description)'}`,
                suggestion: 'Address the pending work or track it as a spec.',
            });
        }
    }
    // Deeply nested code (more than 5 levels)
    for (let i = 0; i < lines.length; i++) {
        const nestLine = lines[i];
        if (nestLine === undefined) {
            continue;
        }
        const leadingSpaces = /^(\s*)/.exec(nestLine)?.[1]?.length ?? 0;
        const indentLevel = leadingSpaces / 2; // Assume 2-space indent
        if (indentLevel > 5 && nestLine.trim().length > 0) {
            findings.push({
                file,
                line: i + 1,
                category: 'clean-code',
                severity: 'warning',
                rule: 'deep-nesting',
                message: `Code is nested ${Math.floor(indentLevel)} levels deep`,
                suggestion: 'Use early returns, extract functions, or refactor conditional logic.',
            });
            break; // Report only first occurrence per file
        }
    }
    // Magic numbers (numeric literals outside of obvious contexts)
    let magicNumberFound = false;
    for (let i = 0; i < lines.length && !magicNumberFound; i++) {
        const line = lines[i];
        if (line === undefined) {
            continue;
        }
        if (line.trim().startsWith('//') ||
            line.trim().startsWith('#') ||
            line.trim().startsWith('*')) {
            continue;
        }
        if (line.includes('import ') || line.includes('require(')) {
            continue;
        }
        const magicNumbers = line.matchAll(/(?<!\w)(?<!\.)(\d{2,})(?!\w|\.|\d)/g);
        for (const match of magicNumbers) {
            const matchGroup = match[1];
            /* v8 ignore next 2 */
            if (matchGroup === undefined) {
                continue;
            }
            const num = parseInt(matchGroup, 10);
            if ([0, 1, 2, 100, 200, 201, 204, 301, 302, 400, 401, 403, 404, 500].includes(num)) {
                continue;
            }
            if (line.includes('port') || line.includes('PORT') || line.includes('timeout')) {
                continue;
            }
            findings.push({
                file,
                line: i + 1,
                category: 'clean-code',
                severity: 'info',
                rule: 'magic-number',
                message: `Magic number ${num} found. Consider extracting to a named constant.`,
                suggestion: 'Define as a named constant to improve readability and maintainability.',
            });
            magicNumberFound = true;
            break;
        }
    }
    return findings;
}
// === DRY Checks ===
export function checkDry(file, _content, lines) {
    const findings = [];
    const lineHashes = new Map();
    for (let i = 0; i < lines.length; i++) {
        const dryLine = lines[i];
        /* v8 ignore next 3 */
        if (dryLine === undefined) {
            continue;
        }
        const trimmed = dryLine.trim();
        if (trimmed.length < 10) {
            continue;
        }
        if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('*')) {
            continue;
        }
        const existing = lineHashes.get(trimmed);
        if (existing) {
            existing.push(i + 1);
        }
        else {
            lineHashes.set(trimmed, [i + 1]);
        }
    }
    for (const [line, occurrences] of lineHashes) {
        if (occurrences.length >= 4) {
            if (/^[{})\];,]$/.exec(line) || /^return\s/.exec(line) || /^break;?$/.exec(line)) {
                continue;
            }
            findings.push({
                file,
                category: 'dry',
                severity: 'info',
                rule: 'duplicate-code',
                message: `Line "${line.substring(0, 60)}${line.length > 60 ? '...' : ''}" appears ${occurrences.length} times.`,
                suggestion: 'Extract duplicated logic into a reusable function or constant.',
            });
            break;
        }
    }
    return findings;
}
// === Custom Rule Check ===
export function checkCustomRule(file, content, rule) {
    if (!rule.pattern) {
        return null;
    }
    try {
        const regex = new RegExp(rule.pattern, 'gi');
        if (regex.test(content)) {
            return {
                file,
                category: rule.category,
                severity: rule.severity,
                rule: rule.id,
                message: rule.description,
                suggestion: `Custom rule from: ${rule.source}`,
            };
        }
    }
    catch {
        // Invalid regex pattern
    }
    return null;
}
// === Function Boundary Detector (shared helper) ===
export function findFunctionBoundaries(lines) {
    const functions = [];
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        /* v8 ignore next 3 */
        if (line === undefined) {
            continue;
        }
        const fnMatch = /(?:(?:export\s+)?(?:async\s+)?function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>|def\s+(\w+)|func\s+(\w+)|fn\s+(\w+)|fun\s+(\w+))/.exec(line);
        if (fnMatch) {
            const name = fnMatch[1] ??
                fnMatch[2] ??
                fnMatch[3] ??
                fnMatch[4] ??
                fnMatch[5] ??
                fnMatch[6] ??
                'anonymous';
            let braceCount = 0;
            let started = false;
            let endLine = i;
            for (let j = i; j < lines.length; j++) {
                const braces = lines[j];
                /* v8 ignore next 3 */
                if (braces === undefined) {
                    continue;
                }
                for (const ch of braces) {
                    if (ch === '{') {
                        braceCount++;
                        started = true;
                    }
                    if (ch === '}') {
                        braceCount--;
                    }
                }
                if (started && braceCount <= 0) {
                    endLine = j;
                    break;
                }
                if (/def\s/.exec(line) && j > i && braces.trim().length > 0) {
                    const currentIndent = /^(\s*)/.exec(braces)?.[1]?.length ?? 0;
                    const fnIndent = /^(\s*)/.exec(line)?.[1]?.length ?? 0;
                    if (currentIndent <= fnIndent && j > i + 1) {
                        endLine = j - 1;
                        break;
                    }
                }
                endLine = j;
            }
            functions.push({
                name,
                startLine: i + 1,
                length: endLine - i + 1,
            });
        }
    }
    return functions;
}
//# sourceMappingURL=auditor-code-quality.js.map