/**
 * Pass #88: naming-convention (category: maintainability)
 *
 * Checks that class, interface, method, and field names follow the
 * established conventions for each language. Violations are purely
 * structural — no corpus statistics or LLM required.
 *
 * Conventions enforced:
 *   Java / TypeScript
 *     • Class name     → PascalCase  (`^[A-Z][A-Za-z0-9]*$`)
 *     • Interface name → PascalCase; the `I`-prefix anti-pattern is
 *       configurable (opt-in, off by default — many codebases use it
 *       intentionally; enable via `passOptions.namingConvention.enforceIPrefix`)
 *     • Method name    → camelCase   (`^[a-z][a-zA-Z0-9]*$`)
 *     • Constant field → UPPER_SNAKE_CASE  (field with final/static/const modifier)
 *
 *   Python
 *     • Class name     → PascalCase  (`^[A-Z][A-Za-z0-9]*$`)
 *     • Method name    → snake_case  (`^[a-z_][a-z0-9_]*$`)
 *
 *   Bash / Rust
 *     • Function name  → snake_case  (`^[a-z_][a-z0-9_]*$`)
 *
 * Skip rules (to reduce false positives):
 *   • Names ≤ 2 characters
 *   • Names starting with `_` (private convention)
 *   • Names starting with `$` (JS framework injections)
 *   • Dunder methods: `__init__`, `__str__`, etc.
 *   • Common single-letter generics: T, K, V, E
 *   • Java/TS main entry: `main`
 *
 * Capped at 20 findings per file to avoid noise.
 */
const PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
const CAMEL_CASE_RE = /^[a-z][a-zA-Z0-9]*$/;
const SNAKE_CASE_RE = /^[a-z_][a-z0-9_]*$/;
const UPPER_SNAKE_RE = /^[A-Z][A-Z0-9_]*$/;
const I_PREFIX_RE = /^I[A-Z]/;
const DUNDER_RE = /^__\w+__$/;
/** Single-letter generic type parameters that are always exempt. */
const GENERIC_NAMES = new Set(['T', 'K', 'V', 'E', 'R', 'N', 'S', 'U', 'W']);
/** Method names that are conventional overrides and exempt from camelCase. */
const EXEMPT_METHODS = new Set([
    'main', 'toString', 'hashCode', 'equals', 'compareTo', 'valueOf',
    'of', 'from', 'create', 'build', 'get', 'set', 'is', 'has',
]);
const MAX_FINDINGS = 20;
function shouldSkipName(name) {
    if (name.length <= 2)
        return true;
    if (name.startsWith('_') || name.startsWith('$'))
        return true;
    if (DUNDER_RE.test(name))
        return true;
    if (GENERIC_NAMES.has(name))
        return true;
    // Skip synthetic/internal names generated by the parser (e.g., <module>, <anonymous>)
    if (name.startsWith('<') || name.endsWith('>'))
        return true;
    return false;
}
export class NamingConventionPass {
    name = 'naming-convention';
    category = 'maintainability';
    enforceIPrefix;
    constructor(options = {}) {
        this.enforceIPrefix = options.enforceIPrefix ?? false;
    }
    run(ctx) {
        const { graph, language } = ctx;
        const file = graph.ir.meta.file;
        const violations = [];
        let findingCount = 0;
        const addViolation = (entity, name, line, expected, message) => {
            if (findingCount >= MAX_FINDINGS)
                return;
            violations.push({ entity, name, line, expected, actual: name });
            findingCount++;
            ctx.addFinding({
                id: `naming-convention-${file}-${line}-${name}`,
                pass: this.name,
                category: this.category,
                rule_id: this.name,
                severity: 'low',
                level: 'note',
                message,
                file,
                line,
                fix: `Rename \`${name}\` to follow ${expected} convention`,
                evidence: { name, expected_convention: expected },
            });
        };
        for (const type of graph.ir.types) {
            if (findingCount >= MAX_FINDINGS)
                break;
            if (shouldSkipName(type.name))
                continue;
            if (language === 'java' || language === 'typescript' || language === 'javascript') {
                // Class names → PascalCase
                if (type.kind === 'class' && !PASCAL_CASE_RE.test(type.name)) {
                    addViolation('class', type.name, type.start_line, 'PascalCase', `Class \`${type.name}\` should be PascalCase`);
                }
                // Interface names → PascalCase; I-prefix check is opt-in
                if (type.kind === 'interface') {
                    if (!PASCAL_CASE_RE.test(type.name)) {
                        addViolation('interface', type.name, type.start_line, 'PascalCase', `Interface \`${type.name}\` should be PascalCase`);
                    }
                    else if (this.enforceIPrefix && I_PREFIX_RE.test(type.name)) {
                        addViolation('interface', type.name, type.start_line, 'PascalCase (no I-prefix)', `Interface \`${type.name}\` uses I-prefix which is not idiomatic — prefer plain PascalCase`);
                    }
                }
                // Method names → camelCase
                for (const method of type.methods) {
                    if (findingCount >= MAX_FINDINGS)
                        break;
                    if (shouldSkipName(method.name))
                        continue;
                    if (EXEMPT_METHODS.has(method.name))
                        continue;
                    if (!CAMEL_CASE_RE.test(method.name)) {
                        addViolation('method', method.name, method.start_line, 'camelCase', `Method \`${type.name}.${method.name}()\` should be camelCase`);
                    }
                }
                // Constant fields → UPPER_SNAKE_CASE
                if (language === 'java') {
                    for (const field of type.fields) {
                        if (findingCount >= MAX_FINDINGS)
                            break;
                        if (shouldSkipName(field.name))
                            continue;
                        const isConstant = field.modifiers.includes('final') && field.modifiers.includes('static');
                        if (isConstant && !UPPER_SNAKE_RE.test(field.name)) {
                            addViolation('field', field.name, type.start_line, 'UPPER_SNAKE_CASE', `Static final field \`${field.name}\` should be UPPER_SNAKE_CASE`);
                        }
                    }
                }
            }
            else if (language === 'python') {
                // Python class names → PascalCase
                if (type.kind === 'class' && !PASCAL_CASE_RE.test(type.name)) {
                    addViolation('class', type.name, type.start_line, 'PascalCase', `Class \`${type.name}\` should be PascalCase`);
                }
                // Python method names → snake_case
                for (const method of type.methods) {
                    if (findingCount >= MAX_FINDINGS)
                        break;
                    if (shouldSkipName(method.name))
                        continue;
                    if (DUNDER_RE.test(method.name))
                        continue;
                    if (!SNAKE_CASE_RE.test(method.name)) {
                        addViolation('method', method.name, method.start_line, 'snake_case', `Method \`${type.name}.${method.name}()\` should be snake_case`);
                    }
                }
            }
            else if (language === 'bash' || language === 'rust') {
                // Bash/Rust function names → snake_case
                for (const method of type.methods) {
                    if (findingCount >= MAX_FINDINGS)
                        break;
                    if (shouldSkipName(method.name))
                        continue;
                    if (!SNAKE_CASE_RE.test(method.name)) {
                        addViolation('method', method.name, method.start_line, 'snake_case', `Function \`${method.name}\` should be snake_case`);
                    }
                }
            }
        }
        return { violations };
    }
}
//# sourceMappingURL=naming-convention-pass.js.map