/**
 * Hallucination Checker
 *
 * Detects non-existent imports, fabricated APIs, and incorrect patterns
 * commonly produced by AI code generation.
 *
 * @module scanners/ai-code/hallucination-checker
 */
import { readFile, access } from "fs/promises";
import { dirname, join, resolve } from "path";
import { glob } from "glob";
const KNOWN_FABRICATED_PACKAGES = new Set([
    "react-native-awesome-button",
    "super-fast-json",
    "ultra-validator",
    "mega-state-manager",
    "hyper-fetch",
    "quantum-router",
]);
const DEPRECATED_APIS = {
    "componentWillMount": { replacement: "componentDidMount or useEffect", since: "React 16.3" },
    "componentWillReceiveProps": { replacement: "getDerivedStateFromProps or useEffect", since: "React 16.3" },
    "componentWillUpdate": { replacement: "getSnapshotBeforeUpdate", since: "React 16.3" },
    "findDOMNode": { replacement: "refs", since: "React 18" },
    "createFactory": { replacement: "JSX", since: "React 18" },
    "substr": { replacement: "substring or slice", since: "ES2022" },
    "escape": { replacement: "encodeURIComponent", since: "ES3" },
    "unescape": { replacement: "decodeURIComponent", since: "ES3" },
};
const MIXED_FRAMEWORK_PATTERNS = [
    {
        frameworks: ["React", "Vue"],
        pattern: /import\s+.*from\s+['"]react['"][\s\S]{0,500}import\s+.*from\s+['"]vue['"]/,
        description: "Mixing React and Vue imports",
    },
    {
        frameworks: ["React", "Angular"],
        pattern: /import\s+.*from\s+['"]react['"][\s\S]{0,500}import\s+.*from\s+['"]@angular/,
        description: "Mixing React and Angular imports",
    },
    {
        frameworks: ["Express", "Fastify"],
        pattern: /import\s+.*from\s+['"]express['"][\s\S]{0,500}import\s+.*from\s+['"]fastify['"]/,
        description: "Mixing Express and Fastify server frameworks",
    },
];
export function parseImports(content, filePath) {
    const imports = [];
    const lines = content.split("\n");
    const importRegex = /^import\s+(?:(\{[^}]+\}|\*\s+as\s+\w+|\w+)(?:\s*,\s*(\{[^}]+\}|\*\s+as\s+\w+|\w+))?)\s+from\s+['"]([^'"]+)['"]/;
    const requireRegex = /(?:const|let|var)\s+(?:(\{[^}]+\})|(\w+))\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/;
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i].trim();
        let match = line.match(importRegex);
        if (match) {
            const source = match[3];
            const specifierPart = match[1] + (match[2] ? `, ${match[2]}` : "");
            const specifiers = extractSpecifiers(specifierPart);
            imports.push({
                source,
                specifiers,
                line: i + 1,
                isRelative: source.startsWith(".") || source.startsWith("/"),
                isPackage: !source.startsWith(".") && !source.startsWith("/"),
            });
            continue;
        }
        match = line.match(requireRegex);
        if (match) {
            const source = match[3];
            const specifierPart = match[1] ?? match[2] ?? "";
            const specifiers = extractSpecifiers(specifierPart);
            imports.push({
                source,
                specifiers,
                line: i + 1,
                isRelative: source.startsWith(".") || source.startsWith("/"),
                isPackage: !source.startsWith(".") && !source.startsWith("/"),
            });
        }
    }
    return imports;
}
function extractSpecifiers(part) {
    if (!part)
        return [];
    const cleaned = part.replace(/[{}]/g, "").trim();
    if (!cleaned)
        return [];
    return cleaned
        .split(",")
        .map((s) => s.trim())
        .filter((s) => s.length > 0)
        .map((s) => s.replace(/\s+as\s+\w+/, "").trim());
}
export async function checkRelativeImport(importInfo, sourceFilePath, projectPath) {
    const sourceDir = dirname(sourceFilePath);
    const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ""];
    const indexFiles = ["index.ts", "index.tsx", "index.js", "index.jsx"];
    for (const ext of extensions) {
        const targetPath = resolve(sourceDir, importInfo.source + ext);
        try {
            await access(targetPath);
            return null;
        }
        catch {
            // Continue checking
        }
    }
    for (const indexFile of indexFiles) {
        const targetPath = resolve(sourceDir, importInfo.source, indexFile);
        try {
            await access(targetPath);
            return null;
        }
        catch {
            // Continue checking
        }
    }
    return {
        type: "nonexistent_import",
        location: {
            file: sourceFilePath,
            line: importInfo.line,
        },
        severity: "high",
        description: `Relative import "${importInfo.source}" does not resolve to an existing file`,
        actual: importInfo.source,
        suggestion: "Verify the import path exists or create the missing module",
    };
}
export async function checkPackageImport(importInfo, projectPath) {
    const packageName = importInfo.source.startsWith("@")
        ? importInfo.source.split("/").slice(0, 2).join("/")
        : importInfo.source.split("/")[0];
    if (KNOWN_FABRICATED_PACKAGES.has(packageName)) {
        return {
            type: "fabricated_package",
            location: {
                file: "",
                line: importInfo.line,
            },
            severity: "critical",
            description: `Package "${packageName}" is a known fabricated/hallucinated package name`,
            actual: packageName,
            suggestion: "This package does not exist on npm. Find a real alternative.",
        };
    }
    try {
        const packageJsonPath = join(projectPath, "package.json");
        const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
        const allDeps = {
            ...packageJson.dependencies,
            ...packageJson.devDependencies,
            ...packageJson.peerDependencies,
        };
        if (packageName in allDeps) {
            return null;
        }
        const nodeBuiltins = new Set([
            "assert", "buffer", "child_process", "cluster", "console", "constants",
            "crypto", "dgram", "dns", "domain", "events", "fs", "http", "https",
            "module", "net", "os", "path", "process", "punycode", "querystring",
            "readline", "repl", "stream", "string_decoder", "sys", "timers",
            "tls", "tty", "url", "util", "v8", "vm", "zlib", "worker_threads",
            "fs/promises", "node:fs", "node:path", "node:url", "node:crypto",
            "node:http", "node:https", "node:stream", "node:buffer", "node:util",
            "node:events", "node:os", "node:child_process", "node:worker_threads",
        ]);
        if (nodeBuiltins.has(packageName) || nodeBuiltins.has(importInfo.source)) {
            return null;
        }
        return {
            type: "nonexistent_import",
            location: {
                file: "",
                line: importInfo.line,
            },
            severity: "medium",
            description: `Package "${packageName}" is not listed in package.json dependencies`,
            actual: packageName,
            suggestion: `Run: npm install ${packageName}`,
        };
    }
    catch {
        return null;
    }
}
export function checkDeprecatedAPIs(content, filePath) {
    const findings = [];
    const lines = content.split("\n");
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        for (const [api, info] of Object.entries(DEPRECATED_APIS)) {
            const pattern = new RegExp(`(?:^|\\s|\\.)${api}\\s*\\(`);
            if (pattern.test(line)) {
                findings.push({
                    type: "deprecated_api",
                    location: {
                        file: filePath,
                        line: i + 1,
                    },
                    severity: "medium",
                    description: `"${api}" is deprecated since ${info.since}`,
                    actual: api,
                    expected: info.replacement,
                    suggestion: `Replace with ${info.replacement}`,
                });
            }
        }
    }
    return findings;
}
export function checkMixedFrameworks(content, filePath) {
    const findings = [];
    for (const pattern of MIXED_FRAMEWORK_PATTERNS) {
        if (pattern.pattern.test(content)) {
            findings.push({
                type: "mixed_frameworks",
                location: {
                    file: filePath,
                    line: 1,
                },
                severity: "high",
                description: pattern.description,
                actual: `${pattern.frameworks[0]} + ${pattern.frameworks[1]}`,
                suggestion: "Choose one framework and remove the other",
            });
        }
    }
    return findings;
}
export async function checkFileForHallucinations(filePath, projectPath, content) {
    const fileContent = content ?? (await readFile(filePath, "utf-8"));
    const findings = [];
    const imports = parseImports(fileContent, filePath);
    for (const importInfo of imports) {
        let finding = null;
        if (importInfo.isRelative) {
            finding = await checkRelativeImport(importInfo, filePath, projectPath);
        }
        else if (importInfo.isPackage) {
            finding = await checkPackageImport(importInfo, projectPath);
        }
        if (finding) {
            finding.location.file = filePath;
            findings.push(finding);
        }
    }
    findings.push(...checkDeprecatedAPIs(fileContent, filePath));
    findings.push(...checkMixedFrameworks(fileContent, filePath));
    return findings;
}
export async function scanDirectoryForHallucinations(projectPath, options = {}) {
    const extensions = options.extensions ?? ["ts", "tsx", "js", "jsx"];
    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 allFindings = [];
    for (const file of files) {
        const findings = await checkFileForHallucinations(file, projectPath);
        allFindings.push(...findings);
    }
    const byType = {};
    const bySeverity = {};
    for (const finding of allFindings) {
        byType[finding.type] = (byType[finding.type] ?? 0) + 1;
        bySeverity[finding.severity] = (bySeverity[finding.severity] ?? 0) + 1;
    }
    return {
        files: files.length,
        findings: allFindings,
        summary: {
            byType: byType,
            bySeverity,
        },
    };
}
export function calculateHallucinationScore(findings) {
    if (findings.length === 0)
        return 100;
    const weights = {
        critical: 25,
        high: 15,
        medium: 8,
        low: 3,
    };
    let penalty = 0;
    for (const finding of findings) {
        penalty += weights[finding.severity] ?? 5;
    }
    return Math.max(0, 100 - penalty);
}
//# sourceMappingURL=hallucination-checker.js.map