// Planu — Over-engineering detector (SPEC-482)
// Detects: single-impl interfaces, factory returning one type, wrapper functions forwarding args unchanged.
import { readFileSync } from 'node:fs';
/** Detect interfaces that appear to have only one implementation class in the file. */
function detectSingleImplInterfaces(source, file) {
    const candidates = [];
    const lines = source.split('\n');
    // Collect all interface names
    const interfaceNames = [];
    lines.forEach((line, idx) => {
        const m = /^(?:export\s+)?interface\s+(\w+)/.exec(line);
        if (m?.[1] !== undefined) {
            interfaceNames.push({ name: m[1], line: idx + 1 });
        }
    });
    for (const iface of interfaceNames) {
        // Count implementations (class Foo implements Bar)
        const implRe = new RegExp(`class\\s+\\w+\\s+implements\\s+${iface.name}\\b`, 'g');
        const matches = source.match(implRe);
        if (matches !== null && matches.length === 1) {
            candidates.push({
                file,
                line: iface.line,
                category: 'over-engineering',
                severity: 'low',
                description: `Interface \`${iface.name}\` has only one implementation in this file.`,
                suggestion: 'Remove the interface and use the concrete type directly, unless polymorphism is genuinely needed.',
            });
        }
    }
    return candidates;
}
/** Detect factory functions that always return the same concrete type. */
function detectSingleTypeFactories(source, file) {
    const candidates = [];
    const lines = source.split('\n');
    lines.forEach((line, idx) => {
        // Pattern: function createXxx(...) { return new Xxx( or return { ... }
        const isSingleReturnFactory = /function\s+create\w+\s*\(/.test(line) ||
            /(?:const|let)\s+create\w+\s*=\s*(?:\(|function)/.test(line);
        if (!isSingleReturnFactory) {
            return;
        }
        // Look ahead for multiple return statements
        const body = lines.slice(idx, idx + 30).join('\n');
        const returnMatches = body.match(/\breturn\b/g);
        if (returnMatches !== null && returnMatches.length === 1) {
            candidates.push({
                file,
                line: idx + 1,
                category: 'over-engineering',
                severity: 'low',
                description: 'Factory function appears to return only one type (single `return` in body).',
                suggestion: 'Replace with a direct constructor call or object literal.',
            });
        }
    });
    return candidates;
}
/** Strip TypeScript type annotations from a parameter list, leaving only names. */
function stripTypes(params) {
    // Remove ": SomeType" annotations and generic type params
    return params
        .split(',')
        .map((p) => p.trim().split(':')[0]?.trim() ?? '')
        .filter((p) => p.length > 0)
        .join(',');
}
/** Detect wrapper functions that simply forward all arguments to another function. */
function detectWrapperFunctions(source, file) {
    const candidates = [];
    const lines = source.split('\n');
    // Heuristic: a function whose entire body is `return someOtherFn(sameArgs)`
    const wrapperRe = /function\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*\S+\s*)?\{[\s\n]*return\s+(\w+)\s*\(\s*([^)]*)\s*\)\s*;?[\s\n]*\}/;
    for (let i = 0; i < lines.length; i++) {
        const chunk = lines.slice(i, i + 5).join(' ');
        const m = wrapperRe.exec(chunk);
        if (m !== null) {
            // Strip type annotations before comparing params vs call args
            const wrapperParamNames = stripTypes(m[2] ?? '');
            const callArgNames = (m[4] ?? '').replace(/\s/g, '');
            if (wrapperParamNames === callArgNames && wrapperParamNames.length > 0) {
                candidates.push({
                    file,
                    line: i + 1,
                    category: 'wrapper-function',
                    severity: 'medium',
                    description: `Function \`${m[1]}\` only wraps \`${m[3]}\` and forwards arguments unchanged.`,
                    suggestion: `Call \`${m[3]}\` directly and delete \`${m[1]}\`.`,
                });
            }
        }
    }
    return candidates;
}
/** Detect hardcoded feature flags (boolean constants always true/false). */
function detectPermanentFeatureFlags(source, file) {
    const candidates = [];
    const lines = source.split('\n');
    lines.forEach((line, idx) => {
        // const FEATURE_xxx = true/false or ENABLE_xxx = true/false
        const m = /(?:const|let|var)\s+((?:FEATURE|ENABLE|DISABLE|FLAG)_\w+)\s*=\s*(true|false)\b/.exec(line);
        if (m?.[1] !== undefined) {
            candidates.push({
                file,
                line: idx + 1,
                category: 'permanent-feature-flag',
                severity: 'medium',
                description: `Permanent feature flag \`${m[1]}\` is hardcoded to \`${m[2]}\`.`,
                suggestion: `Remove the flag and inline the ${m[2] === 'true' ? 'enabled' : 'disabled'} code path.`,
            });
        }
    });
    return candidates;
}
/** Run all over-engineering detectors on the given files. */
export function detectOverEngineering(filePaths) {
    const candidates = [];
    for (const fp of filePaths) {
        let source;
        try {
            source = readFileSync(fp, 'utf8');
        }
        catch {
            continue;
        }
        candidates.push(...detectSingleImplInterfaces(source, fp), ...detectSingleTypeFactories(source, fp), ...detectWrapperFunctions(source, fp), ...detectPermanentFeatureFlags(source, fp));
    }
    return candidates;
}
//# sourceMappingURL=over-engineering-detector.js.map