/**
 * AI Code Detector
 *
 * Detects patterns that indicate AI-generated code.
 *
 * @module scanners/ai-code/ai-detector
 */
import { readFile } from "fs/promises";
import { glob } from "glob";
const AI_PATTERNS = [
    // Comment patterns indicating AI generation
    {
        name: "ai-comment-generated",
        regex: /\/\/\s*(generated|created)\s*(by|with|using)\s*(ai|gpt|claude|copilot|chatgpt|openai|anthropic)/i,
        confidence: "high",
        type: "comment",
        description: "Comment explicitly mentions AI generation",
    },
    {
        name: "ai-comment-assistant",
        regex: /\/\/\s*(ai|assistant|copilot)\s*suggestion/i,
        confidence: "high",
        type: "comment",
        description: "Comment mentions AI assistant suggestion",
    },
    {
        name: "todo-placeholder",
        regex: /\/\/\s*TODO:\s*(implement|add|fix)\s+(this|here|logic)/i,
        confidence: "medium",
        type: "comment",
        description: "Generic TODO placeholder typical of AI generation",
    },
    {
        name: "verbose-explanation",
        regex: /\/\/\s*This\s+(function|method|class)\s+(is\s+used\s+to|will|should)\s+.{50,}/i,
        confidence: "medium",
        type: "comment",
        description: "Overly verbose explanatory comment",
    },
    // Structural patterns
    {
        name: "excessive-try-catch",
        regex: /try\s*\{[\s\S]{10,50}\}\s*catch\s*\(\s*\w+\s*\)\s*\{\s*console\.(log|error)\(/,
        confidence: "low",
        type: "structure",
        description: "Minimal try-catch with only console logging",
    },
    {
        name: "redundant-async",
        regex: /async\s+\w+\s*\([^)]*\)\s*\{[^}]*return\s+[^a]*;[^}]*\}/,
        confidence: "low",
        type: "structure",
        description: "Async function without await",
    },
    // Naming patterns
    {
        name: "overly-descriptive-name",
        regex: /const\s+\w{30,}\s*=/,
        confidence: "low",
        type: "naming",
        description: "Excessively long variable name",
    },
    {
        name: "generic-handler-name",
        regex: /(const|function)\s+(handleClick|handleSubmit|handleChange|handleInput)\s*[=\(]/,
        confidence: "low",
        type: "naming",
        description: "Generic event handler name",
    },
    // Code patterns
    {
        name: "repeated-null-checks",
        regex: /(\?\.\w+){4,}/,
        confidence: "medium",
        type: "pattern",
        description: "Excessive optional chaining",
    },
    {
        name: "empty-implementation",
        regex: /\{\s*(\/\/\s*TODO|throw\s+new\s+Error\s*\(\s*["']Not\s+implemented["']\s*\))\s*\}/,
        confidence: "medium",
        type: "pattern",
        description: "Empty implementation placeholder",
    },
    // Metadata patterns
    {
        name: "cursor-metadata",
        regex: /@cursor-ai|cursor:ignore|cursor-generated/i,
        confidence: "high",
        type: "metadata",
        description: "Cursor AI metadata marker",
    },
    {
        name: "copilot-metadata",
        regex: /@copilot|copilot-ignore|copilot-generated/i,
        confidence: "high",
        type: "metadata",
        description: "GitHub Copilot metadata marker",
    },
];
export async function detectAIPatterns(filePath, content) {
    const fileContent = content ?? (await readFile(filePath, "utf-8"));
    const lines = fileContent.split("\n");
    const matches = [];
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        for (const pattern of AI_PATTERNS) {
            const match = line.match(pattern.regex);
            if (match) {
                matches.push({
                    pattern: pattern.name,
                    file: filePath,
                    line: i + 1,
                    match: match[0],
                    confidence: pattern.confidence,
                });
            }
        }
    }
    return matches;
}
export function patternMatchesToIndicators(matches) {
    const patternInfo = new Map(AI_PATTERNS.map((p) => [p.name, p]));
    return matches.map((match) => {
        const info = patternInfo.get(match.pattern);
        return {
            type: info?.type ?? "pattern",
            location: {
                file: match.file,
                line: match.line,
            },
            description: info?.description ?? `Matched pattern: ${match.pattern}`,
            confidence: match.confidence,
            evidence: match.match,
        };
    });
}
export async function scanDirectoryForAIPatterns(projectPath, options = {}) {
    const extensions = options.extensions ?? ["ts", "tsx", "js", "jsx", "py", "go", "rb"];
    const exclude = options.exclude ?? ["node_modules", "dist", ".git", "build", "coverage"];
    const pattern = `**/*.{${extensions.join(",")}}`;
    const files = await glob(pattern, {
        cwd: projectPath,
        ignore: exclude.map((e) => `**/${e}/**`),
        absolute: true,
    });
    const allMatches = [];
    for (const file of files) {
        const matches = await detectAIPatterns(file);
        allMatches.push(...matches);
    }
    return {
        files: files.length,
        matches: allMatches,
        indicators: patternMatchesToIndicators(allMatches),
    };
}
export function calculateAILikelihood(indicators) {
    if (indicators.length === 0)
        return 0;
    const weights = {
        high: 25,
        medium: 15,
        low: 5,
        uncertain: 2,
    };
    let score = 0;
    for (const indicator of indicators) {
        score += weights[indicator.confidence];
    }
    return Math.min(100, score);
}
export function aggregateByFile(indicators) {
    const fileMap = new Map();
    for (const indicator of indicators) {
        const existing = fileMap.get(indicator.location.file) ?? [];
        existing.push(indicator);
        fileMap.set(indicator.location.file, existing);
    }
    const result = new Map();
    const confidenceOrder = ["high", "medium", "low", "uncertain"];
    for (const [file, fileIndicators] of fileMap) {
        const maxConfidence = confidenceOrder.find((c) => fileIndicators.some((i) => i.confidence === c)) ?? "uncertain";
        result.set(file, {
            count: fileIndicators.length,
            maxConfidence,
            likelihood: calculateAILikelihood(fileIndicators),
        });
    }
    return result;
}
//# sourceMappingURL=ai-detector.js.map