import fs from "node:fs/promises";
import fss from "node:fs";
import matter from "gray-matter";
import { debug } from "../core/logger.js";
/**
 * 审计知识库健康度。
 * 检查五个维度: 陈旧条目、重复触发词、格式规范、类型覆盖、内容完整度。
 * @param knowledgeIndex - 知识索引管理器
 * @param config - 项目配置
 * @returns 审计结果，包含发现项和统计摘要
 */
export async function auditKnowledge(knowledgeIndex, _config) {
    debug("知识审计", "开始审计知识库健康度");
    const { global: globalEntries, project: projectEntries } = knowledgeIndex.getAllEntries();
    const all = [...globalEntries, ...projectEntries];
    const findings = [];
    const now = Date.now();
    let findingId = 0;
    // 1. 陈旧条目: 超过 90 天未使用或从未被匹配
    for (const entry of all) {
        if (entry.status === "deprecated")
            continue;
        if (entry.usage_count === 0) {
            findings.push({
                id: `AUD-${String(++findingId).padStart(3, "0")}`,
                severity: "info",
                category: "stale",
                entry_name: entry.name,
                message: `从未被匹配使用: ${entry.name}`,
                suggestion: "检查 when 触发词是否覆盖实际场景，或标记为 deprecated",
            });
        }
        else if (entry.last_used_at) {
            const daysSinceUse = (now - new Date(entry.last_used_at).getTime()) / (1000 * 60 * 60 * 24);
            if (daysSinceUse > 90) {
                findings.push({
                    id: `AUD-${String(++findingId).padStart(3, "0")}`,
                    severity: "warning",
                    category: "stale",
                    entry_name: entry.name,
                    message: `超过 ${Math.floor(daysSinceUse)} 天未被使用: ${entry.name}`,
                    suggestion: "评估是否仍需要，考虑更新触发词或标记为 deprecated",
                });
            }
        }
    }
    // 2. 重复检查: 相似触发词
    for (let i = 0; i < all.length; i++) {
        for (let j = i + 1; j < all.length; j++) {
            if (all[i].type !== all[j].type)
                continue;
            const overlap = keywordOverlap(all[i].when, all[j].when);
            if (overlap >= 0.6) {
                findings.push({
                    id: `AUD-${String(++findingId).padStart(3, "0")}`,
                    severity: "info",
                    category: "duplicate",
                    entry_name: `${all[i].name} / ${all[j].name}`,
                    message: `触发词高度重叠 (${Math.round(overlap * 100)}%): "${all[i].name}" 和 "${all[j].name}"`,
                    suggestion: "考虑合并或区分触发词",
                });
            }
        }
    }
    // 3. 格式检查: 缺失必需的 frontmatter 字段 + Schema 验证
    for (const entry of all) {
        if (entry.status === "deprecated")
            continue;
        const issues = [];
        if (!entry.when || entry.when.trim().length === 0)
            issues.push("when");
        if (!entry.scope || entry.scope.length === 0)
            issues.push("scope");
        if (!entry.products || entry.products.length === 0)
            issues.push("products");
        if (issues.length > 0) {
            findings.push({
                id: `AUD-${String(++findingId).padStart(3, "0")}`,
                severity: "warning",
                category: "format",
                entry_name: entry.name,
                message: `缺少必需字段: ${issues.join(", ")}`,
                suggestion: `补充 ${issues.join("、")} 字段`,
            });
        }
        // 3b. Schema 验证: 读取原始文件进行深度检查
        const schemaIssues = await validateFormatSchema(entry);
        for (const issue of schemaIssues) {
            findings.push({
                id: `AUD-${String(++findingId).padStart(3, "0")}`,
                severity: "info",
                category: "format",
                entry_name: entry.name,
                message: issue.message,
                suggestion: issue.suggestion,
            });
        }
    }
    // 4. 覆盖度检查: 确保各知识类型都有条目
    const requiredTypes = ["pattern", "procedure", "domain", "review_rule"];
    for (const type of requiredTypes) {
        const count = all.filter((e) => e.type === type && e.status !== "deprecated").length;
        if (count === 0) {
            findings.push({
                id: `AUD-${String(++findingId).padStart(3, "0")}`,
                severity: "warning",
                category: "coverage",
                entry_name: `type=${type}`,
                message: `缺少 ${type} 类型的知识条目`,
                suggestion: `新增至少一个 ${type} 条目`,
            });
        }
    }
    // 5. 完整度检查: 检查规则/验收项数量是否达到类型阈值
    for (const entry of all) {
        if (entry.status === "deprecated")
            continue;
        if (!entry.file_path || !fss.existsSync(entry.file_path))
            continue;
        const thresholds = COMPLETENESS_THRESHOLDS[entry.type];
        if (!thresholds)
            continue;
        let raw;
        try {
            raw = await fs.readFile(entry.file_path, "utf-8");
        }
        catch {
            continue;
        }
        const { content: body } = matter(raw);
        for (const check of thresholds) {
            const count = countPattern(body, check.pattern);
            if (count < check.minimum) {
                findings.push({
                    id: `AUD-${String(++findingId).padStart(3, "0")}`,
                    severity: "info",
                    category: "completeness",
                    entry_name: entry.name,
                    message: `${entry.type} 条目"${entry.name}"的 ${check.field} 不足：实际 ${count} 条，要求 ≥ ${check.minimum} 条`,
                    suggestion: `运行 sf_knowledge_update 补充 ${check.field}，或调用 sf_knowledge_add 的 auto_enrich 机制自动探索行业最佳实践`,
                });
            }
        }
    }
    const summary = {
        stale: findings.filter((f) => f.category === "stale").length,
        duplicate: findings.filter((f) => f.category === "duplicate").length,
        format: findings.filter((f) => f.category === "format").length,
        coverage: findings.filter((f) => f.category === "coverage").length,
        completeness: findings.filter((f) => f.category === "completeness").length,
        design_drift: 0,
    };
    debug("知识审计", `完成，共 ${findings.length} 项发现`);
    return {
        total_entries: all.filter((e) => e.status !== "deprecated").length,
        findings,
        summary,
        audited_at: new Date().toISOString(),
    };
}
// ── 格式 Schema ──
/** 规范的 frontmatter 字段顺序 */
const CANONICAL_FM_ORDER = ["name", "type", "when", "scope", "products"];
/** 各知识类型的格式规范定义 */
const FORMAT_SCHEMA = {
    pattern: {
        required_headings: ["## 决策规则", "## 验收项"],
    },
    domain: {
        required_headings: ["## 业务规则", "## 约束条件", "## 例外情况", "## 关联模式"],
    },
    procedure: {
        required_headings: ["## 适用场景", "## 步骤", "## 检查点", "## 注意事项"],
    },
    pipeline_procedure: {
        required_headings: [],
        partial_match_headings: ["### 第"],
    },
    review_rule: {
        required_headings: [],
        partial_match_headings: ["pattern:", "severity:", "description:"],
    },
    acceptance_template: {
        required_headings: [],
    },
};
/** 各知识类型的完整度阈值（字段名、匹配正则、最小数量） */
const COMPLETENESS_THRESHOLDS = {
    pattern: [
        { field: "决策规则", pattern: /^- /gm, minimum: 5 },
        { field: "验收项", pattern: /^- \[AC-/gm, minimum: 3 },
    ],
    domain: [
        { field: "业务规则", pattern: /^(?:- |\d+\.)/gm, minimum: 6 },
    ],
    procedure: [
        { field: "步骤", pattern: /^\d+\.\s/gm, minimum: 5 },
        { field: "检查点", pattern: /^- \[ \]/gm, minimum: 3 },
    ],
    pipeline_procedure: [
        { field: "阶段", pattern: /^### 第\d+步/gm, minimum: 3 },
    ],
    review_rule: [
        { field: "规则", pattern: /^## [A-Z]+-\d+/gm, minimum: 5 },
    ],
};
/**
 * 统计正则匹配数量。
 * @param body - 文本内容
 * @param pattern - 正则表达式
 * @returns 匹配数量
 */
function countPattern(body, pattern) {
    return (body.match(pattern) ?? []).length;
}
/**
 * 验证知识条目的格式 Schema 规范。
 * 检查 frontmatter 字段顺序、when 字段风格、body 必需标题。
 * @param entry - 知识条目
 * @returns 格式问题列表
 */
async function validateFormatSchema(entry) {
    const results = [];
    if (!entry.file_path || !fss.existsSync(entry.file_path))
        return results;
    let raw;
    try {
        raw = await fs.readFile(entry.file_path, "utf-8");
    }
    catch {
        return results;
    }
    const { data: fm, content: body } = matter(raw);
    // 检查 frontmatter 字段顺序
    const fmKeys = Object.keys(fm);
    const orderIssues = [];
    for (let i = 0; i < CANONICAL_FM_ORDER.length; i++) {
        const expected = CANONICAL_FM_ORDER[i];
        if (i < fmKeys.length && fmKeys[i] !== expected) {
            orderIssues.push(`期望第 ${i + 1} 个字段为 ${expected}，实际为 ${fmKeys[i]}`);
            break;
        }
    }
    if (orderIssues.length > 0) {
        results.push({
            message: `frontmatter 字段顺序不规范: ${orderIssues[0]}`,
            suggestion: `标准顺序: ${CANONICAL_FM_ORDER.join(" → ")}`,
        });
    }
    // 检查 when 字段风格: 第一个逗号分隔段应 ≤ 10 个中文字符
    const whenStr = fm.when ?? "";
    const firstSegment = whenStr.split(/[,\s，]/)[0] ?? "";
    const chineseCharCount = (firstSegment.match(/[一-鿿]/g) ?? []).length;
    if (chineseCharCount > 10 && firstSegment.length > 12) {
        results.push({
            message: `when 字段首个触发词过长（${chineseCharCount} 个中文字符），疑似描述性短语: "${firstSegment}"`,
            suggestion: "when 应为纯关键词逗号分隔，如: 数据导出, CSV, Excel, 批量导出",
        });
    }
    // 检查 body 标题
    const schema = FORMAT_SCHEMA[entry.type];
    if (schema) {
        for (const heading of schema.required_headings) {
            if (!body.includes(heading)) {
                results.push({
                    message: `body 缺少必需标题: ${heading}（type=${entry.type}）`,
                    suggestion: `按格式规约补充 ${heading} 部分`,
                });
            }
        }
        if (schema.partial_match_headings) {
            for (const partial of schema.partial_match_headings) {
                if (!body.includes(partial)) {
                    results.push({
                        message: `body 缺少必需内容: "${partial}"（type=${entry.type}）`,
                        suggestion: `按格式规约补充包含 "${partial}" 的内容`,
                    });
                }
            }
        }
    }
    return results;
}
// ── 辅助函数 ──
/**
 * 计算两个关键词字符串的重叠度。
 * @param a - 第一个关键词字符串
 * @param b - 第二个关键词字符串
 * @returns 重叠度（0-1），1 表示完全重叠
 */
function keywordOverlap(a, b) {
    const wordsA = new Set(a.toLowerCase().split(/[,\s，]+/).filter(Boolean));
    const wordsB = new Set(b.toLowerCase().split(/[,\s，]+/).filter(Boolean));
    if (wordsA.size === 0 || wordsB.size === 0)
        return 0;
    let overlap = 0;
    for (const w of wordsA) {
        if (wordsB.has(w))
            overlap++;
    }
    return overlap / Math.min(wordsA.size, wordsB.size);
}
//# sourceMappingURL=knowledge_health.js.map