/**
 * 代码级偷懒模式检测器 — 纯正则实现，零 AI 依赖。
 *
 * 职责：
 * - 对 .ts 文件内容执行 6 种偷懒模式检测
 * - 返回 LazyPatternFinding[]，包含严重级别、行号、中文描述和证据片段
 * - 支持 abstract/interface/test 文件豁免
 *
 * 检测维度：
 * 1. 空函数体（hard_fail）— 排除 abstract/interface 声明
 * 2. 未实现占位 throw new Error("not implemented")（hard_fail）
 * 3. 空 catch 块（hard_fail）
 * 4. TODO/FIXME 注释（warning）— .test.ts 豁免
 * 5. 未实现注释（待实现/待完成）（hard_fail）— .test.ts 豁免
 * 6. any 无 disable 注释（warning）— .d.ts 豁免
 *
 * 设计决策：
 * - 代码围栏状态机跳过 markdown 代码块（防止误报）
 * - 逐行匹配，每行只报第一个匹配（避免同一行重复报告）
 * - 豁免逻辑在函数入口通过 filePath 后缀判断
 */
const PATTERNS = [
    // 1. 空函数体 — hard_fail（只匹配 { 和 } 同行的真正空体）
    {
        name: "empty_function_body",
        regex: /(?:function\s+\w+\s*\([^)]*\)\s*\{\s*\}|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*=>\s*\{\s*\})/,
        severity: "hard_fail",
        message_template: "空函数体：{match}",
        exclude_line: /abstract\s|interface\s|\bdeclare\s/,
    },
    // 2. 未实现占位 — hard_fail
    {
        name: "not_implemented_placeholder",
        regex: /throw\s+new\s+(?:Error|TypeError|NotImplementedError)\s*\(\s*["'](?:not\s+implemented|TODO|FIXME|unimplemented|暂未实现|尚未实现)/i,
        severity: "hard_fail",
        message_template: "未实现占位：{match}",
    },
    // 3. 空 catch 块 — hard_fail
    {
        name: "empty_catch_block",
        regex: /catch\s*\(\s*\w+\s*\)\s*\{\s*\}/,
        severity: "hard_fail",
        message_template: "空 catch 块：{match}",
    },
    // 4. TODO/FIXME 注释 — warning
    {
        name: "todo_fixme_comment",
        regex: /(?:\/\/|#)\s*(?:TODO|FIXME|HACK|XXX)\b/i,
        severity: "warning",
        message_template: "发现 TODO/FIXME 注释：{match}",
        exclude_file_suffix: [".test.ts", ".spec.ts"],
    },
    // 5. 未实现注释 — hard_fail
    {
        name: "unimplemented_comment",
        regex: /(?:\/\/|#)\s*(?:待实现|待完成|待补充|暂未实现|尚未实现|需要实现|待开发)/,
        severity: "hard_fail",
        message_template: "发现未实现注释：{match}",
        exclude_file_suffix: [".test.ts", ".spec.ts"],
    },
    // 6. any 无 disable — warning
    {
        name: "any_without_disable",
        regex: /:\s*any\b/,
        severity: "warning",
        message_template: "使用 any 类型但无 eslint-disable 注释：{match}",
        exclude_line: /eslint-disable.*@typescript-eslint\/no-explicit-any/,
        exclude_file_suffix: [".d.ts"],
    },
];
// ── 代码围栏状态机 ──
/**
 * 判断某行是否在 markdown 代码围栏内。
 * 用于跳过 .ts 文件中嵌入的 markdown 文档（如注释中的代码示例）。
 */
function isInCodeFence(line, fenceState) {
    const trimmed = line.trim();
    if (/^```/.test(trimmed)) {
        fenceState.inFence = !fenceState.inFence;
        return true; // 围栏行本身也跳过
    }
    return fenceState.inFence;
}
// ── 核心检测函数 ──
/**
 * 对 .ts 文件内容执行偷懒模式检测。
 *
 * @param content 文件内容
 * @param filePath 文件路径（用于豁免判断）
 * @returns 偷懒模式发现项数组
 */
export function detectLazyPatterns(content, filePath) {
    const findings = [];
    const lines = content.split("\n");
    const fenceState = { inFence: false };
    // ── 第一遍：检测跨行空函数体 ──
    // function foo() {\n} 和 const foo = () => {\n};
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i].trim();
        // 跳过 abstract/interface/declare 声明
        if (/\b(?:abstract|interface|declare)\b/.test(line))
            continue;
        // 匹配 function 开头并在行尾有 { 的声明
        const funcOpen = /^(?:async\s+)?function\s+\w+\s*\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*\{\s*$/.exec(line);
        // 匹配箭头函数开头并在行尾有 { 的声明
        const arrowOpen = /^(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?\([^)]*\)\s*(?::\s*\w+(?:<[^>]+>)?)?\s*=>\s*\{\s*$/.exec(line);
        if (funcOpen || arrowOpen) {
            // 检查下一非空行是否只有 }
            let nextIdx = i + 1;
            while (nextIdx < lines.length && lines[nextIdx].trim() === "")
                nextIdx++;
            if (nextIdx < lines.length) {
                const nextTrimmed = lines[nextIdx].trim();
                if (nextTrimmed === "}" || nextTrimmed === "};") {
                    findings.push({
                        pattern: "empty_function_body",
                        severity: "hard_fail",
                        line_number: i + 1,
                        message: `空函数体：${line}`,
                        evidence: line.length > 120 ? line.substring(0, 120) + "..." : line,
                    });
                }
            }
        }
    }
    // ── 第二遍：逐行模式匹配 ──
    const emptyBodyLines = new Set(findings.map((f) => f.line_number));
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        // 已在第一遍报过的行跳过
        if (emptyBodyLines.has(i + 1))
            continue;
        // 跳过 markdown 代码围栏内的行
        if (isInCodeFence(line, fenceState))
            continue;
        // 逐模式匹配，每行只报第一个匹配
        for (const pattern of PATTERNS) {
            // 文件豁免检查
            if (pattern.exclude_file_suffix) {
                if (pattern.exclude_file_suffix.some((sfx) => filePath.endsWith(sfx))) {
                    continue;
                }
            }
            const match = pattern.regex.exec(line);
            if (!match)
                continue;
            // 行级排除检查
            if (pattern.exclude_line && pattern.exclude_line.test(line))
                continue;
            // 截断证据到 120 字符
            const evidence = line.length > 120 ? line.substring(0, 120) + "..." : line;
            findings.push({
                pattern: pattern.name,
                severity: pattern.severity,
                line_number: i + 1,
                message: pattern.message_template.replace("{match}", match[0]),
                evidence,
            });
            break; // 每行只报第一个匹配
        }
    }
    return findings;
}
//# sourceMappingURL=lazy_pattern_detector.js.map