/**
 * R8: 现有系统分析器 — L1/L2/L3 多层级分析。
 *
 * 层级:
 * - L1: 目录结构扫描 + 技术栈指纹检测（项目指纹）
 * - L2: 架构拓扑 — 模块边界、import 依赖图、分层、耦合
 * - L3: 代码谱系 — 数据模型追踪、API 调用链、变更影响半径
 *
 * 所有解析使用正则表达式（无完整 AST）。结果为结构化数据供下游消费者使用。
 */
import fs from "node:fs";
import path from "node:path";
import { debug } from "../core/logger.js";
import { safeJsonParse } from "../core/helpers.js";
// ── 辅助函数 ──
const DEFAULT_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
const DEFAULT_SOURCE_DIRS = ["src"];
const DEFAULT_MAX_FILES = 500;
function resolveOptions(options) {
    return {
        sourceDirs: options?.sourceDirs ?? DEFAULT_SOURCE_DIRS,
        extensions: options?.extensions ?? DEFAULT_EXTENSIONS,
        maxFiles: options?.maxFiles ?? DEFAULT_MAX_FILES,
    };
}
/**
 * 在 sourceDirs 中递归收集匹配扩展名的文件。
 */
function collectSourceFiles(projectPath, sourceDirs, extensions, maxFiles) {
    const files = [];
    function walk(dir) {
        if (files.length >= maxFiles)
            return;
        let entries;
        try {
            entries = fs.readdirSync(dir, { withFileTypes: true });
        }
        catch {
            return;
        }
        for (const entry of entries) {
            if (files.length >= maxFiles)
                return;
            // 跳过 node_modules, dist, .git, coverage
            if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git" || entry.name === "coverage") {
                continue;
            }
            const full = path.join(dir, entry.name);
            if (entry.isDirectory()) {
                walk(full);
            }
            else if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
                files.push(full);
            }
        }
    }
    for (const srcDir of sourceDirs) {
        const dir = path.resolve(projectPath, srcDir);
        if (fs.existsSync(dir)) {
            walk(dir);
        }
    }
    return files;
}
/**
 * 安全读取文件内容。失败时返回空字符串。
 */
function readFileContent(filePath) {
    try {
        return fs.readFileSync(filePath, "utf-8");
    }
    catch {
        return "";
    }
}
// ── L1: 项目指纹 ──
/**
 * L1 分析: 目录结构扫描 + 技术栈指纹检测。
 */
export function analyzeL1(projectPath, options) {
    const opts = resolveOptions(options);
    const files = collectSourceFiles(projectPath, opts.sourceDirs, opts.extensions, opts.maxFiles);
    debug("existing_system_analyzer", "L1: collected %d source files from %s", files.length, projectPath);
    // 收集目录（相对于项目根目录）
    const dirSet = new Set();
    for (const f of files) {
        const rel = path.relative(projectPath, path.dirname(f));
        if (rel)
            dirSet.add(rel);
    }
    const directories = [...dirSet].sort();
    // 技术栈检测
    const techStack = [];
    const packageJsonPath = path.join(projectPath, "package.json");
    let hasPackageJson = false;
    let packageDependencies = [];
    if (fs.existsSync(packageJsonPath)) {
        hasPackageJson = true;
        try {
            const pkg = safeJsonParse(fs.readFileSync(packageJsonPath, "utf-8"));
            if (!pkg)
                throw new Error("parse failed");
            const allDeps = [
                ...Object.keys(pkg.dependencies ?? {}),
                ...Object.keys(pkg.devDependencies ?? {}),
            ];
            packageDependencies = allDeps;
            if (allDeps.includes("typescript")) {
                techStack.push({ name: "typescript", evidence: "package.json devDependencies", confidence: "high" });
            }
            if (allDeps.some(d => d === "vitest" || d === "jest")) {
                techStack.push({ name: "vitest/jest", evidence: "package.json test framework", confidence: "high" });
            }
            if (allDeps.some(d => d.startsWith("@vue") || d === "vue")) {
                techStack.push({ name: "vue", evidence: "package.json vue dependencies", confidence: "high" });
            }
            if (allDeps.includes("react") || allDeps.some(d => d.startsWith("@react"))) {
                techStack.push({ name: "react", evidence: "package.json react dependencies", confidence: "high" });
            }
            if (allDeps.includes("express") || allDeps.includes("fastify") || allDeps.includes("koa")) {
                techStack.push({ name: "node-http-server", evidence: "package.json server framework", confidence: "high" });
            }
            if (allDeps.includes("handlebars")) {
                techStack.push({ name: "handlebars", evidence: "package.json template engine", confidence: "high" });
            }
        }
        catch {
            // package.json 解析失败，跳过
        }
    }
    const tsconfigPath = path.join(projectPath, "tsconfig.json");
    const hasTsconfig = fs.existsSync(tsconfigPath);
    if (hasTsconfig) {
        techStack.push({ name: "typescript-config", evidence: "tsconfig.json exists", confidence: "high" });
    }
    // 从文件扩展名检测
    const hasTsx = files.some(f => f.endsWith(".tsx"));
    if (hasTsx) {
        techStack.push({ name: "react-jsx", evidence: ".tsx files detected", confidence: "medium" });
    }
    // 源文件数量
    const sourceFileCount = files.length;
    // 总文件数量（源目录中所有文件）
    const allFiles = collectSourceFiles(projectPath, opts.sourceDirs, [".ts", ".tsx", ".js", ".jsx", ".json", ".yaml", ".yml", ".md", ".css", ".html"], opts.maxFiles * 2);
    return {
        level: "L1",
        project_path: projectPath,
        directories,
        tech_stack: techStack,
        file_count: allFiles.length,
        source_file_count: sourceFileCount,
        has_package_json: hasPackageJson,
        has_tsconfig: hasTsconfig,
        package_dependencies: packageDependencies,
    };
}
// ── L2: 架构拓扑 ──
// 提取 import/require 目标的正则
const IMPORT_REGEX = /(?:import\s+.*?from\s+['"](.+?)['"]|require\s*\(\s*['"](.+?)['"]\s*\))/g;
/**
 * 从源文件中提取所有 import 目标。
 * 返回相对路径（如它们在 import 语句中出现的形式）。
 */
function extractImports(content) {
    const imports = [];
    let match;
    // 重置全局正则的 lastIndex
    IMPORT_REGEX.lastIndex = 0;
    while ((match = IMPORT_REGEX.exec(content)) !== null) {
        const importPath = match[1] || match[2];
        if (importPath && !importPath.startsWith("node:")) {
            // 只保留相对路径的 import（以 . 或 .. 开头）
            if (importPath.startsWith(".")) {
                imports.push(importPath);
            }
        }
    }
    return imports;
}
/**
 * 提导出 function/class 名称的正则。
 */
const EXPORT_REGEX = /export\s+(?:function|class|const|interface|type|enum)\s+(\w+)/g;
function extractExports(content) {
    const names = [];
    let match;
    EXPORT_REGEX.lastIndex = 0;
    while ((match = EXPORT_REGEX.exec(content)) !== null) {
        names.push(match[1]);
    }
    return names;
}
/**
 * 根据文件相对于源码根目录的路径推断模块名称。
 * 模块是源码根目录下的第一个有意义的子目录。
 * 例如: src/engine/foo.ts -> "engine"
 * 例如: src/engine/gate_checks/bar.ts -> "engine/gate_checks"
 */
function inferModule(filePath, projectPath, sourceDirs) {
    const rel = path.relative(projectPath, filePath);
    for (const srcDir of sourceDirs) {
        const prefix = srcDir.endsWith("/") ? srcDir : srcDir + "/";
        if (rel.startsWith(prefix)) {
            const afterSrc = rel.slice(prefix.length);
            const parts = afterSrc.split("/");
            // 返回前 1-2 个目录层级
            if (parts.length <= 2)
                return parts[0].replace(/\.\w+$/, "");
            // 按前两个层级分组子模块
            return parts.slice(0, 2).join("/");
        }
    }
    // 回退: 使用第一个目录组件
    const parts = rel.split("/");
    return parts.length > 1 ? parts.slice(0, 2).join("/") : rel;
}
/**
 * 将相对 import 路径解析为磁盘上的实际文件（近似）。
 * 尝试原路径、追加扩展名，并处理 ESM `.js` -> `.ts` 映射。
 */
function resolveImportPath(importPath, fromFile, _projectPath) {
    const fromDir = path.dirname(fromFile);
    const resolved = path.resolve(fromDir, importPath);
    // 辅助: 检查路径是否为已有文件
    const isFile = (p) => {
        try {
            return fs.existsSync(p) && fs.statSync(p).isFile();
        }
        catch {
            return false;
        }
    };
    const isDir = (p) => {
        try {
            return fs.existsSync(p) && fs.statSync(p).isDirectory();
        }
        catch {
            return false;
        }
    };
    // 尝试精确路径
    if (isFile(resolved))
        return resolved;
    // ESM 风格: import "./foo.js" 但实际文件是 "./foo.ts"
    if (resolved.endsWith(".js")) {
        const asTs = resolved.replace(/\.js$/, ".ts");
        if (isFile(asTs))
            return asTs;
        const asTsx = resolved.replace(/\.js$/, ".tsx");
        if (isFile(asTsx))
            return asTsx;
    }
    // 尝试追加扩展名
    for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
        const withExt = resolved + ext;
        if (isFile(withExt))
            return withExt;
    }
    // 尝试作为带 index 文件的目录
    if (isDir(resolved)) {
        for (const idx of ["index.ts", "index.tsx", "index.js"]) {
            const indexPath = path.join(resolved, idx);
            if (isFile(indexPath))
                return indexPath;
        }
    }
    return null;
}
/**
 * 定义常见模块类型的简单分层顺序。
 * 数字越小 = 层级越高。"层级违规"是指低层级从高层级 import。
 */
const LAYER_ORDER = [
    { pattern: /(?:route|router|controller|handler|endpoint)/i, layer: 1, name: "presentation" },
    { pattern: /(?:service|usecase|use.case|business|domain|logic)/i, layer: 2, name: "business" },
    { pattern: /(?:repository|repo|data|store|dao|model|persist|database|db)/i, layer: 3, name: "data" },
];
function classifyLayer(moduleName) {
    for (const l of LAYER_ORDER) {
        if (l.pattern.test(moduleName))
            return l;
    }
    return null;
}
/**
 * L2 分析: 架构拓扑 — 模块边界、依赖图、分层、耦合。
 */
export function analyzeL2(projectPath, options) {
    const opts = resolveOptions(options);
    const files = collectSourceFiles(projectPath, opts.sourceDirs, opts.extensions, opts.maxFiles);
    debug("existing_system_analyzer", "L2: analyzing %d source files", files.length);
    // 阶段 1: 构建模块映射
    const moduleMap = new Map();
    for (const file of files) {
        const modName = inferModule(file, projectPath, opts.sourceDirs);
        const existing = moduleMap.get(modName);
        if (existing) {
            existing.file_count++;
            const content = readFileContent(file);
            existing.exported_names.push(...extractExports(content));
        }
        else {
            const content = readFileContent(file);
            moduleMap.set(modName, {
                name: modName,
                path: path.relative(projectPath, path.dirname(file)),
                file_count: 1,
                exported_names: extractExports(content),
            });
        }
    }
    // 阶段 2: 构建依赖图
    const depEdgeMap = new Map();
    const layerViolations = [];
    // 追踪每个模块的 import 用于耦合计算
    const moduleImportsFrom = new Map();
    for (const file of files) {
        const srcModule = inferModule(file, projectPath, opts.sourceDirs);
        const content = readFileContent(file);
        const imports = extractImports(content);
        if (!moduleImportsFrom.has(srcModule)) {
            moduleImportsFrom.set(srcModule, new Set());
        }
        const srcDeps = moduleImportsFrom.get(srcModule);
        for (const imp of imports) {
            const resolvedPath = resolveImportPath(imp, file, projectPath);
            if (!resolvedPath)
                continue;
            const targetModule = inferModule(resolvedPath, projectPath, opts.sourceDirs);
            if (targetModule === srcModule)
                continue; // intra-module import, skip
            srcDeps.add(targetModule);
            // 构建边
            const edgeKey = `${srcModule}->${targetModule}`;
            const existing = depEdgeMap.get(edgeKey);
            if (existing) {
                existing.weight++;
                if (existing.sample_paths.length < 3) {
                    existing.sample_paths.push(imp);
                }
            }
            else {
                depEdgeMap.set(edgeKey, {
                    from: srcModule,
                    to: targetModule,
                    weight: 1,
                    sample_paths: [imp],
                });
            }
            // 检查层级违规
            const srcLayer = classifyLayer(srcModule);
            const tgtLayer = classifyLayer(targetModule);
            if (srcLayer && tgtLayer && srcLayer.layer > tgtLayer.layer) {
                // 低层级从高层级 import
                layerViolations.push({
                    source_module: srcModule,
                    target_module: targetModule,
                    source_file: path.relative(projectPath, file),
                    import_path: imp,
                    description: `${srcLayer.name}(${srcModule}) depends on ${tgtLayer.name}(${targetModule}) — potential layer violation`,
                });
            }
        }
    }
    // 阶段 3: 计算耦合指标
    const afferent = {};
    const efferent = {};
    // 初始化所有模块
    for (const modName of moduleMap.keys()) {
        afferent[modName] = 0;
        efferent[modName] = 0;
    }
    // 统计传出（efferent）耦合
    for (const [mod, deps] of moduleImportsFrom) {
        efferent[mod] = deps.size;
        // 目标的传入（afferent）计数
        for (const dep of deps) {
            afferent[dep] = (afferent[dep] ?? 0) + 1;
        }
    }
    // 查找热点（被依赖最多的前 5 个模块）
    const hotspots = Object.entries(afferent)
        .sort((a, b) => b[1] - a[1])
        .slice(0, 5)
        .map(([module, count]) => ({ module, afferent: count }));
    return {
        level: "L2",
        project_path: projectPath,
        modules: [...moduleMap.values()],
        dependency_graph: [...depEdgeMap.values()].sort((a, b) => b.weight - a.weight),
        layer_violations: layerViolations,
        coupling_metrics: {
            afferent,
            efferent,
            hotspots,
        },
        source_files_scanned: files.length,
    };
}
// ── L3: 代码谱系 ──
// type/interface 声明的正则
const TYPE_INTERFACE_REGEX = /(?:export\s+)?(?:interface|type)\s+(\w+)/g;
// 路由定义的正则（Express、Fastify、Koa 模式）
const ROUTE_REGEX = /(?:app|router|server|fastify)\s*\.\s*(get|post|put|delete|patch|all)\s*\(\s*['"`]([^'"`]+)['"`]/g;
// 备选: function handler 模式
const HANDLER_REGEX = /(?:async\s+)?function\s+(\w+)\s*\(\s*(?:req|request|ctx|context)/g;
/**
 * L3 分析: 代码谱系 — 数据模型追踪、API 调用链、变更影响半径。
 */
export function analyzeL3(projectPath, options) {
    const opts = resolveOptions(options);
    const files = collectSourceFiles(projectPath, opts.sourceDirs, opts.extensions, opts.maxFiles);
    debug("existing_system_analyzer", "L3: analyzing %d source files", files.length);
    // 阶段 1: 构建 type/interface 索引
    const typeDefinitions = new Map();
    const typeUsages = new Map();
    for (const file of files) {
        const content = readFileContent(file);
        const relPath = path.relative(projectPath, file);
        let match;
        TYPE_INTERFACE_REGEX.lastIndex = 0;
        while ((match = TYPE_INTERFACE_REGEX.exec(content)) !== null) {
            const typeName = match[1];
            typeDefinitions.set(typeName, { file: relPath, content });
        }
    }
    // 阶段 2: 通过 import 追踪类型使用
    for (const file of files) {
        const content = readFileContent(file);
        const relPath = path.relative(projectPath, file);
        for (const [typeName] of typeDefinitions) {
            // 检查此类型名是否出现在文件中（粗略启发式）
            if (content.includes(typeName) && typeDefinitions.get(typeName).file !== relPath) {
                const existing = typeUsages.get(typeName);
                if (existing) {
                    if (!existing.includes(relPath))
                        existing.push(relPath);
                }
                else {
                    typeUsages.set(typeName, [relPath]);
                }
            }
        }
    }
    // 构建数据模型信息
    const dataModels = [];
    for (const [typeName, def] of typeDefinitions) {
        // 启发式: 如果 interface 有多个属性（字段声明）则视为"实体型"
        // 匹配多行（前导空白）和内联模式如: { field: type; }
        const fieldPatterns = def.content.match(/(?:readonly\s+)?\w+\s*[?:]/g);
        const fieldCount = fieldPatterns ? fieldPatterns.length : 0;
        const usedIn = typeUsages.get(typeName) ?? [];
        // 仅包含在至少一个其他文件中被使用或具有实体型结构的类型
        if (usedIn.length > 0 || fieldCount >= 3) {
            dataModels.push({
                name: typeName,
                defined_in: def.file,
                used_in: usedIn,
                is_entity_like: fieldCount >= 5,
            });
        }
    }
    // 阶段 3: 构建 API 调用链
    const apiCallChains = [];
    for (const file of files) {
        const content = readFileContent(file);
        const relPath = path.relative(projectPath, file);
        let routeMatch;
        ROUTE_REGEX.lastIndex = 0;
        while ((routeMatch = ROUTE_REGEX.exec(content)) !== null) {
            const httpMethod = routeMatch[1];
            const routePath = `${httpMethod.toUpperCase()} ${routeMatch[2]}`;
            // 查找 handler: 在路由定义后寻找下一个 function/callback
            const afterRoute = content.slice(routeMatch.index + routeMatch[0].length, routeMatch.index + routeMatch[0].length + 500);
            // 尝试查找 handler 名称
            let handlerName = "anonymous";
            const handlerMatch = afterRoute.match(/(?:async\s+)?(?:function\s+)?(\w+)/);
            if (handlerMatch) {
                handlerName = handlerMatch[1];
            }
            // 从此文件的 import 收集下游文件
            const imports = extractImports(content);
            const downstream = [];
            for (const imp of imports) {
                const resolved = resolveImportPath(imp, file, projectPath);
                if (resolved) {
                    downstream.push(path.relative(projectPath, resolved));
                }
            }
            apiCallChains.push({
                route_path: routePath,
                route_file: relPath,
                handler_name: handlerName,
                downstream_files: downstream,
            });
        }
        // 同时检查 handler 模式
        HANDLER_REGEX.lastIndex = 0;
        let handlerMatch;
        while ((handlerMatch = HANDLER_REGEX.exec(content)) !== null) {
            // 避免与基于路由的检测重复
            const handlerName = handlerMatch[1];
            if (!apiCallChains.some(c => c.handler_name === handlerName && c.route_file === relPath)) {
                const imports = extractImports(content);
                const downstream = [];
                for (const imp of imports) {
                    const resolved = resolveImportPath(imp, file, projectPath);
                    if (resolved) {
                        downstream.push(path.relative(projectPath, resolved));
                    }
                }
                apiCallChains.push({
                    route_path: `handler:${handlerName}`,
                    route_file: relPath,
                    handler_name: handlerName,
                    downstream_files: downstream,
                });
            }
        }
    }
    // 阶段 4: 估算变更影响半径
    const changeImpact = [];
    // 构建文件级 import 图用于传递分析
    const fileImports = new Map();
    const fileDependedUpon = new Map();
    for (const file of files) {
        const content = readFileContent(file);
        const relPath = path.relative(projectPath, file);
        const imports = extractImports(content);
        const resolvedImports = [];
        for (const imp of imports) {
            const resolved = resolveImportPath(imp, file, projectPath);
            if (resolved) {
                const resolvedRel = path.relative(projectPath, resolved);
                resolvedImports.push(resolvedRel);
                // 追踪反向依赖
                const existing = fileDependedUpon.get(resolvedRel);
                if (existing) {
                    if (!existing.includes(relPath))
                        existing.push(relPath);
                }
                else {
                    fileDependedUpon.set(resolvedRel, [relPath]);
                }
            }
        }
        fileImports.set(relPath, resolvedImports);
    }
    // 计算每个文件的影响
    for (const [filePath] of fileImports) {
        const directDeps = fileDependedUpon.get(filePath) ?? [];
        const directCount = directDeps.length;
        // 传递依赖：依赖本文件所依赖的文件（2 跳）
        const transitiveSet = new Set(directDeps);
        for (const dep of directDeps) {
            const depsOfDep = fileDependedUpon.get(dep) ?? [];
            for (const d of depsOfDep) {
                if (d !== filePath)
                    transitiveSet.add(d);
            }
        }
        changeImpact.push({
            file: filePath,
            direct_dependents: directCount,
            transitive_dependents: transitiveSet.size,
            risk: directCount >= 10 ? "high" : directCount >= 3 ? "medium" : "low",
        });
    }
    // 按影响排序（从高到低）
    changeImpact.sort((a, b) => b.transitive_dependents - a.transitive_dependents);
    return {
        level: "L3",
        project_path: projectPath,
        data_models: dataModels,
        api_call_chains: apiCallChains,
        change_impact: changeImpact,
        source_files_scanned: files.length,
    };
}
// ── 主入口 ──
/**
 * Analyze existing system at the specified level.
 *
 * - L1: Fast project fingerprint (directory structure + tech stack)
 * - L2: Architecture topology (modules, dependency graph, layering, coupling)
 * - L3: Code lineage (data models, API chains, change impact)
 */
export function analyzeExistingSystem(projectPath, level, options) {
    debug("existing_system_analyzer", "analyzeExistingSystem level=%s path=%s", level, projectPath);
    switch (level) {
        case "L1":
            return analyzeL1(projectPath, options);
        case "L2":
            return analyzeL2(projectPath, options);
        case "L3":
            return analyzeL3(projectPath, options);
    }
}
/**
 * Automatically select analysis level based on project complexity.
 *
 * - < 20 source files: L1
 * - 20-100 source files: L2
 * - > 100 source files: L3
 */
export function selectAnalysisLevel(projectPath, options) {
    const opts = resolveOptions(options);
    const files = collectSourceFiles(projectPath, opts.sourceDirs, opts.extensions, opts.maxFiles);
    if (files.length > 100)
        return "L3";
    if (files.length >= 20)
        return "L2";
    return "L1";
}
//# sourceMappingURL=existing_system_analyzer.js.map