import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import matter from "gray-matter";
import { debug } from "../core/logger.js";
/**
 * 知识配置读取器 — 从 .md 知识文件中提取决策规则配置。
 * 所有八维防御模块通过此读取器获取阈值和规则，而非硬编码。
 */
/** 知识配置缓存 */
const configCache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 缓存有效期：5 分钟
/** 项目知识库根目录 */
function getKnowledgeRoot(projectPath) {
    return path.join(projectPath ?? process.cwd(), ".soloforge", "knowledge");
}
/** 包内模板知识库根目录 */
function getTemplatesRoot() {
    const thisDir = path.dirname(fileURLToPath(import.meta.url));
    // src/engine/knowledge/ → ../../../ → 项目根 → templates（目录层级映射）
    return path.resolve(thisDir, "..", "..", "..", "templates");
}
/**
 * 将旧的知识路径映射到新的类型优先路径。
 * 模板目录重组后:
 *   patterns/core/X.md → internal/patterns/X.md
 *   rules/X.md         → internal/rules/X.md
 *   procedures/X.md    → procedures/X.md
 *   checklists/X.md    → checklists/X.md
 *   review_rules/X.md  → rules/shared/X.md
 *   domain/X.md        → domain/X.md
 *   product_profiles/X.md → profiles/X.md
 *   acceptance_templates/X.md → artifacts/shared/X.md
 */
function remapLegacyPath(relativePath) {
    // patterns/core/ 映射到 internal/patterns/
    let mapped = relativePath.replace(/^patterns\/core\//, "internal/patterns/");
    // patterns/ 非 core 子目录保持不变
    if (!mapped.startsWith("patterns/core/")) {
        mapped = mapped.replace(/^patterns\/(?!core)/, "patterns/");
    }
    // rules/ 映射到 internal/rules/（但 rules/shared/ 保持不变）
    if (mapped.startsWith("rules/") && !mapped.startsWith("rules/shared/")) {
        mapped = mapped.replace(/^rules\//, "internal/rules/");
    }
    // review_rules/ 映射到 rules/shared/
    mapped = mapped.replace(/^review_rules\//, "rules/shared/");
    // acceptance_templates/ 映射到 artifacts/shared/
    mapped = mapped.replace(/^acceptance_templates\//, "artifacts/shared/");
    // product_profiles/ 映射到 profiles/
    mapped = mapped.replace(/^product_profiles\//, "profiles/");
    return mapped;
}
/**
 * 从知识库中加载指定 .md 文件的 frontmatter 和 body。
 * 支持缓存，5 分钟内复用。
 * @param relativePath - 相对于 knowledge/ 的路径，如 "patterns/core/决策网关.md"
 * @param projectPath - 项目根目录
 * @returns frontmatter data + body，文件不存在时返回 null
 */
export function loadKnowledgeConfig(relativePath, projectPath) {
    debug("KnowledgeConfigLoader", "加载知识配置", relativePath);
    // 缓存 key 必须区分 projectPath，否则不同项目的同名文件会互相污染
    const projectRoot = projectPath ?? process.cwd();
    const cacheKey = `${projectRoot}::${relativePath}`;
    // 优先项目 .soloforge/knowledge，缺失时回退包内 templates
    const projectFile = path.join(getKnowledgeRoot(projectPath), relativePath);
    const templateFile = path.join(getTemplatesRoot(), remapLegacyPath(relativePath));
    const useProject = fs.existsSync(projectFile);
    const filePath = useProject ? projectFile : templateFile;
    if (!fs.existsSync(filePath)) {
        debug("KnowledgeConfigLoader", "知识文件不存在", filePath);
        return null;
    }
    // mtime 检查：项目覆盖文件变更后立即失效，模板文件不可变则用 TTL
    let mtime = 0;
    if (useProject) {
        try {
            mtime = fs.statSync(filePath).mtimeMs;
        }
        catch { /* 忽略 */ }
    }
    const cached = configCache.get(cacheKey);
    if (cached) {
        const mtimeMatch = !useProject || cached.mtime === mtime;
        if (mtimeMatch && Date.now() - cached.loadedAt < CACHE_TTL) {
            return { data: cached.data, body: cached.body };
        }
    }
    try {
        const raw = fs.readFileSync(filePath, "utf-8");
        const { data, content: body } = matter(raw);
        configCache.set(cacheKey, { data: data, body, mtime, loadedAt: Date.now() });
        return { data: data, body };
    }
    catch {
        debug("KnowledgeConfigLoader", "读取知识文件失败", filePath);
        return null;
    }
}
/**
 * 从知识文件的决策规则中提取指定键的数值。
 * 格式: - 键名 = 数值（如 "Confidence_Threshold = 0.95"）
 * @param body - 知识文件正文内容
 * @param key - 要提取的规则键名
 * @returns 匹配到的数值，未找到时返回 undefined
 */
export function extractNumberRule(body, key) {
    const regex = new RegExp(`${key.replace(/_/g, "[_ ]")}\\s*=\\s*([\\d.]+)`, "i");
    const match = body.match(regex);
    return match ? parseFloat(match[1]) : undefined;
}
/**
 * 从知识文件的决策规则中提取指定键的字符串值。
 * @param body - 知识文件正文内容
 * @param key - 要提取的规则键名
 * @returns 匹配到的字符串值，未找到时返回 undefined
 */
export function extractStringRule(body, key) {
    const regex = new RegExp(`${key.replace(/_/g, "[_ ]")}\\s*=\\s*([^\\n,]+)`, "i");
    const match = body.match(regex);
    return match ? match[1].trim() : undefined;
}
/**
 * 从知识文件的决策规则中提取列表值。
 * 格式: - 键名 = 值1 / 值2 / 值3
 * @param body - 知识文件正文内容
 * @param key - 要提取的规则键名
 * @returns 匹配到的字符串列表，未找到时返回空数组
 */
export function extractListRule(body, key) {
    const value = extractStringRule(body, key);
    if (!value)
        return [];
    return value.split(/[/,，、]/).map((v) => v.trim()).filter(Boolean);
}
/**
 * 清除知识配置缓存（测试用）。
 */
export function clearConfigCache() {
    debug("KnowledgeConfigLoader", "清除配置缓存");
    configCache.clear();
}
//# sourceMappingURL=knowledge_config_loader.js.map