/**
 * 行为覆盖率验证器 — 对比设计文档与代码实现的覆盖率。
 *
 * 职责：
 * - 检查 OpenAPI spec 中声明的 response status code 是否在代码中有对应返回
 * - 检查 API 文档中声明的错误码表是否在代码 ErrorCode 枚举中包含
 * - 检查架构文档中声明的接口是否在代码中有对应实现
 *
 * 设计决策：
 * - 所有检测基于纯正则，无 AST 依赖
 * - 所有发现均为 warning 级别（不阻断）
 * - 优雅降级：找不到 OpenAPI 文件 → 返回空数组；无 ErrorCode 枚举 → 跳过
 */
import fss from "node:fs";
import path from "node:path";
// ── 核心检测函数 ──
/**
 * 对比设计文档与代码实现，检查行为覆盖率。
 *
 * @param changedFiles 变更文件列表（相对路径）
 * @param projectPath 项目根目录
 * @returns 行为覆盖率发现项数组
 */
export function verifyBehaviorCoverage(_changedFiles, projectPath) {
    const findings = [];
    // ── 检测 1: OpenAPI response status code 覆盖 ──
    findings.push(...checkOpenApiCoverage(projectPath));
    // ── 检测 2: ErrorCode 枚举覆盖 ──
    findings.push(...checkErrorCodeEnumCoverage(projectPath));
    return findings;
}
/**
 * 检查 OpenAPI spec 中声明的 response status code 是否在代码中有对应返回。
 * 优雅降级：找不到 OpenAPI 文件 → 返回空数组。
 */
function checkOpenApiCoverage(projectPath) {
    const findings = [];
    // 查找 OpenAPI 文件
    const openApiPaths = [
        path.join(projectPath, "docs", "api", "openapi.yml"),
        path.join(projectPath, "docs", "api", "openapi.yaml"),
        path.join(projectPath, "docs", "api", "openapi.json"),
        path.join(projectPath, ".soloforge", "output", "openapi.yml"),
    ];
    let openApiContent;
    for (const p of openApiPaths) {
        if (fss.existsSync(p)) {
            try {
                openApiContent = fss.readFileSync(p, "utf-8");
                break;
            }
            catch {
                // 读取出错 → 跳过
                return [];
            }
        }
    }
    // 优雅降级：找不到 OpenAPI 文件
    if (!openApiContent)
        return [];
    // 提取声明的 status code
    const statusCodePattern = /['"](\d{3})['"]:\s*(?:description|content)/g;
    const declaredStatusCodes = new Set();
    let match;
    while ((match = statusCodePattern.exec(openApiContent)) !== null) {
        const code = match[1];
        // 排除常见的默认 status code（200, 201 等）
        if (code !== "200" && code !== "201" && code !== "204") {
            declaredStatusCodes.add(code);
        }
    }
    if (declaredStatusCodes.size === 0)
        return [];
    // 查找源代码目录
    const srcDirs = ["src", "lib", "app"].map((d) => path.join(projectPath, d));
    const srcDir = srcDirs.find((d) => fss.existsSync(d));
    if (!srcDir)
        return [];
    // 扫描源码中实际返回的 status code
    const foundStatusCodes = new Set();
    try {
        const allFiles = walkDir(srcDir, ".ts");
        for (const file of allFiles) {
            try {
                const content = fss.readFileSync(file, "utf-8");
                // 匹配 .status(code) 或 statusCode: code 或 res.status(code)
                const statusPattern = /(?:\.status|status[_-]?code|statusCode)\s*(?:\(|:)\s*(\d{3})/g;
                let m;
                while ((m = statusPattern.exec(content)) !== null) {
                    foundStatusCodes.add(m[1]);
                }
            }
            catch {
                // 单文件读取失败 → 跳过
            }
        }
    }
    catch {
        // 目录遍历失败 → 跳过
        return [];
    }
    // 对比：声明的但代码中未返回的
    for (const code of declaredStatusCodes) {
        if (!foundStatusCodes.has(code)) {
            findings.push({
                source: "openapi",
                expected: `HTTP ${code}`,
                found: false,
                severity: "warning",
                message: `OpenAPI 声明了 HTTP ${code} 响应，但源码中未找到对应的 status(${code}) 返回`,
            });
        }
    }
    return findings;
}
/**
 * 检查 API 文档中的错误码是否在代码 ErrorCode 枚举中包含。
 * 优雅降级：无 ErrorCode 枚举 → 跳过。
 */
function checkErrorCodeEnumCoverage(projectPath) {
    const findings = [];
    // 查找 ErrorCode 枚举文件
    const srcDir = path.join(projectPath, "src");
    if (!fss.existsSync(srcDir))
        return [];
    let enumContent;
    let enumFilePath;
    try {
        const allFiles = walkDir(srcDir, ".ts");
        for (const file of allFiles) {
            try {
                const content = fss.readFileSync(file, "utf-8");
                if (/enum\s+ErrorCode\b/.test(content) || /enum\s+ErrorCodes\b/.test(content)) {
                    enumContent = content;
                    enumFilePath = file;
                    break;
                }
            }
            catch {
                continue;
            }
        }
    }
    catch {
        return [];
    }
    // 优雅降级：无 ErrorCode 枚举 → 跳过
    if (!enumContent || !enumFilePath)
        return [];
    // 提取枚举成员
    const enumMemberPattern = /^\s*(\w+)\s*[=,]/gm;
    const definedCodes = new Set();
    let match;
    while ((match = enumMemberPattern.exec(enumContent)) !== null) {
        definedCodes.add(match[1]);
    }
    // 查找 API 文档中声明的错误码
    const apiDocPaths = [
        path.join(projectPath, "docs", "api", "error-codes.md"),
        path.join(projectPath, "docs", "architecture", "03-API接口设计.md"),
    ];
    for (const docPath of apiDocPaths) {
        if (!fss.existsSync(docPath))
            continue;
        try {
            const docContent = fss.readFileSync(docPath, "utf-8");
            // 提取文档中的错误码（常见格式：CODE_XXX 或 ERXXX）
            const docCodePattern = /\b([A-Z][A-Z0-9_]{2,})\b/g;
            let m;
            while ((m = docCodePattern.exec(docContent)) !== null) {
                const code = m[1];
                // 排除通用词汇
                if (["GET", "POST", "PUT", "DELETE", "PATCH", "HTTP", "API", "URL", "JSON"].includes(code))
                    continue;
                // 如果文档中出现了看起来像错误码的全大写标识符，但枚举中没有
                if (/^[A-Z][A-Z0-9_]+$/.test(code) && code.length >= 4 && code.includes("_")) {
                    if (!definedCodes.has(code)) {
                        findings.push({
                            source: "error_code_enum",
                            expected: code,
                            found: false,
                            severity: "warning",
                            message: `文档声明了错误码 ${code}，但 ErrorCode 枚举中未定义`,
                        });
                    }
                }
            }
        }
        catch {
            continue;
        }
    }
    return findings;
}
/**
 * 递归遍历目录，收集指定后缀的文件。
 */
function walkDir(dir, suffix) {
    const result = [];
    try {
        const entries = fss.readdirSync(dir, { withFileTypes: true });
        for (const entry of entries) {
            const fullPath = path.join(dir, entry.name);
            if (entry.isDirectory()) {
                // 跳过 node_modules 和 .git
                if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist")
                    continue;
                result.push(...walkDir(fullPath, suffix));
            }
            else if (entry.name.endsWith(suffix)) {
                result.push(fullPath);
            }
        }
    }
    catch {
        // 目录读取失败 → 返回空
    }
    return result;
}
//# sourceMappingURL=behavior_coverage_verifier.js.map