// src/engine/code-scanner/layer-scanner.ts — Scans the 4 implementation layers (SPEC-441)
import { readFile, stat } from 'node:fs/promises';
import { join, relative, basename } from 'node:path';
import { fastFindFilesByExt, fastReadFiles } from '../core-bridge.js';
/**
 * Scan a project directory for evidence of spec implementation across 4 layers.
 */
export async function scanLayers(projectPath, keywords, symbolVariants) {
    const srcDir = join(projectPath, 'src');
    const testsDir = join(projectPath, 'tests');
    const [srcExists, testsExists] = await Promise.all([dirExists(srcDir), dirExists(testsDir)]);
    let srcFiles = [];
    let testFiles = [];
    if (srcExists) {
        srcFiles = fastFindFilesByExt(srcDir, ['ts', 'js', 'tsx', 'jsx']).filter((f) => !f.endsWith('.d.ts'));
    }
    if (testsExists) {
        testFiles = fastFindFilesByExt(testsDir, ['ts', 'js', 'tsx', 'jsx']).filter((f) => !f.endsWith('.d.ts'));
    }
    // Layer 1: files whose names match keywords
    const matchedFiles = srcFiles.filter((f) => keywords.some((kw) => basename(f).toLowerCase().includes(kw)));
    // Layer 2: exported symbols in matched files (and register/index files)
    const registerFiles = srcFiles.filter((f) => /register.*\.ts$|index\.ts$/.test(f));
    const filesToScanForSymbols = [...new Set([...matchedFiles, ...registerFiles])];
    const symbols = findSymbols(filesToScanForSymbols, symbolVariants);
    // Layer 3: test files matching keywords
    const matchedTests = testFiles.filter((f) => keywords.some((kw) => basename(f).toLowerCase().includes(kw)));
    // Layer 4: registrations in license-plans.json and register-*.ts files
    const registrations = await findRegistrations(projectPath, srcFiles, keywords);
    return {
        files: matchedFiles.map((f) => relative(projectPath, f)),
        symbols,
        tests: matchedTests.map((f) => relative(projectPath, f)),
        registrations,
    };
}
// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------
function findSymbols(files, variants) {
    const found = new Set();
    if (files.length > 0) {
        const EXPORT_RE = /^export\s+(?:async\s+)?(?:function|class|const|type|interface)\s+(\w+)/gm;
        for (const file of fastReadFiles(files)) {
            let match;
            while ((match = EXPORT_RE.exec(file.content)) !== null) {
                const name = match[1] ?? '';
                if (variants.some((v) => name.toLowerCase().includes(v.toLowerCase()))) {
                    found.add(name);
                }
            }
            EXPORT_RE.lastIndex = 0;
        }
        return [...found];
    }
    return [...found];
}
async function findRegistrations(projectPath, srcFiles, keywords) {
    const found = new Set();
    // Check license-plans.json for tool names matching keywords
    const licensePath = join(projectPath, 'src', 'config', 'license-plans.json');
    try {
        const content = await readFile(licensePath, 'utf8');
        const json = JSON.parse(content);
        const allTools = [...(json.freeTools ?? []), ...(json.proTools ?? [])];
        for (const tool of allTools) {
            if (keywords.some((kw) => tool.includes(kw))) {
                found.add(`license:${tool}`);
            }
        }
    }
    catch {
        // not a planu project — skip
    }
    // Check register-*.ts files for registerTool calls matching keywords
    const registerFiles = srcFiles.filter((f) => /register.*\.ts$/.test(f));
    if (registerFiles.length > 0) {
        const REGISTER_RE = /registerTool\s*\(\s*['"]([^'"]+)['"]/g;
        for (const file of fastReadFiles(registerFiles)) {
            let match;
            while ((match = REGISTER_RE.exec(file.content)) !== null) {
                const toolName = match[1] ?? '';
                if (keywords.some((kw) => toolName.includes(kw))) {
                    found.add(`registered:${toolName}`);
                }
            }
            REGISTER_RE.lastIndex = 0;
        }
        return [...found];
    }
    return [...found];
}
async function dirExists(p) {
    try {
        const s = await stat(p);
        return s.isDirectory();
    }
    catch {
        return false;
    }
}
//# sourceMappingURL=layer-scanner.js.map