import fs from "node:fs/promises";
import fss from "node:fs";
import path from "node:path";
import matter from "gray-matter";
import { debug } from "../engine/core/logger.js";
/**
 * 知识文件写入器。
 * @description 负责知识条目的暂存写入、激活发布和暂存管理。
 *              使用 .soloforge/staged 目录作为暂存区，支持原子写入（先写 .tmp 再 rename）。
 *              包含维度6知识目录隔离机制，禁止将 AI 进化产物写入 core/ 只读目录。
 */
export class KnowledgeWriter {
    stagedDir;
    /**
     * 创建知识写入器实例。
     * @param projectPath - 项目根目录路径
     */
    constructor(projectPath) {
        this.stagedDir = path.join(projectPath, ".soloforge", "staged");
        fss.mkdirSync(this.stagedDir, { recursive: true });
    }
    /**
     * 将知识条目写入暂存区。
     * @description 生成带时间戳的文件名，构建 frontmatter，使用原子写入保存到暂存目录。
     * @param entry - 知识条目元数据
     * @param content - 知识正文内容
     * @returns 暂存文件的绝对路径
     */
    async writeToStaged(entry, content) {
        debug("知识写入", `写入暂存区 — ${entry.name}`);
        const filename = `${entry.type}-${new Date().toISOString().split("T")[0]}-${Date.now()}.md`;
        const filePath = path.join(this.stagedDir, filename);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any -- MCP SDK 桥接/动态 JSON 解析，字段由运行时决定
        const frontmatter = {
            name: entry.name,
            type: entry.type,
            when: entry.when,
            scope: entry.scope,
            products: entry.products,
            status: "staged",
            confidence: entry.confidence,
            created_at: new Date().toISOString().split("T")[0],
            updated_at: new Date().toISOString().split("T")[0],
            usage_count: 0,
        };
        if (entry.tech_stack)
            frontmatter.tech_stack = entry.tech_stack;
        const fileContent = matter.stringify(content, frontmatter);
        const tmpPath = `${filePath}.tmp`;
        await fs.writeFile(tmpPath, fileContent, "utf-8");
        await fs.rename(tmpPath, filePath);
        return filePath;
    }
    /**
     * 将暂存条目提升为活跃状态。
     * @description 从暂存区读取条目，更新状态为 active，移动到知识目录的对应子目录。
     *              包含维度6知识目录隔离检查，禁止写入 core/ 只读目录。
     *              目标文件写入成功后才删除源文件，确保原子性。
     * @param stagedPath - 暂存文件的绝对路径
     * @param knowledgeDir - 目标知识目录路径
     * @returns 激活后的文件绝对路径
     * @throws 当目标目录为 core/ 只读目录时抛出错误
     */
    async promoteToActive(stagedPath, knowledgeDir) {
        debug("知识写入", `提升为活跃 — ${stagedPath}`);
        const content = await fs.readFile(stagedPath, "utf-8");
        const { data, content: body } = matter(content);
        // 维度6: 知识目录隔离 — 禁止将 AI 进化产物写入 core/ 目录
        const type = data.type || "pattern";
        const targetSubdir = this.typeToSubdir(type);
        if (targetSubdir.includes("/core") || targetSubdir.endsWith("/core")) {
            debug("知识写入", `隔离检查失败 — 禁止写入 core/ 目录 — ${targetSubdir}`);
            throw new Error("知识隔离: 禁止将 AI 进化产物写入 core/ 只读目录。目标: " + targetSubdir);
        }
        const targetDir = path.join(knowledgeDir, targetSubdir);
        fss.mkdirSync(targetDir, { recursive: true });
        const filename = path.basename(stagedPath);
        const targetPath = path.join(targetDir, filename);
        // 将状态更新为 active
        data.status = "active";
        const updated = matter.stringify(body, data);
        const tmpPath = `${targetPath}.tmp`;
        await fs.writeFile(tmpPath, updated, "utf-8");
        await fs.rename(tmpPath, targetPath);
        // 从暂存区移除（target 写入成功后才删除源文件）
        await fs.unlink(stagedPath);
        debug("知识写入", `提升完成 — ${targetPath}`);
        return targetPath;
    }
    /**
     * 列出暂存区中的所有知识条目。
     * @description 扫描暂存目录中的所有 .md 文件，解析 frontmatter 提取名称和置信度。
     * @returns 暂存条目列表，包含路径、名称和置信度
     */
    async listStaged() {
        debug("知识写入", "列出暂存条目");
        const results = [];
        try {
            const files = await fs.readdir(this.stagedDir);
            for (const f of files.filter((f) => f.endsWith(".md"))) {
                try {
                    const content = await fs.readFile(path.join(this.stagedDir, f), "utf-8");
                    const { data } = matter(content);
                    results.push({
                        path: path.join(this.stagedDir, f),
                        name: data.name || f,
                        confidence: data.confidence ?? 0,
                    });
                }
                catch {
                    // 跳过无法解析的文件
                }
            }
        }
        catch {
            // 暂存目录不存在
        }
        return results;
    }
    /**
     * 拒绝并删除暂存条目。
     * @description 从暂存区删除指定的暂存文件。
     * @param stagedPath - 暂存文件的绝对路径
     * @returns 是否成功删除（文件不存在或删除失败返回 false）
     */
    async rejectStaged(stagedPath) {
        try {
            await fs.access(stagedPath);
            await fs.unlink(stagedPath);
            debug("知识写入", `已拒绝暂存条目 — ${stagedPath}`);
            return true;
        }
        catch {
            return false;
        }
    }
    /**
     * 根据知识条目类型映射到目标子目录（内部方法）。
     * @param type - 知识条目类型
     * @returns 目标子目录相对路径
     */
    typeToSubdir(type) {
        switch (type) {
            case "pattern": return "patterns/temp";
            case "procedure": return "procedures";
            case "domain": return "domain";
            case "acceptance_template": return "acceptance_templates";
            case "review_rule": return "review_rules";
            case "governance_rule": return "review_rules/governance";
            default: return "patterns/temp";
        }
    }
    /**
     * 检查文件路径是否在 core/ 只读目录中（静态方法）。
     * @description 维度6: 知识目录隔离检查，用于防止将 AI 产物写入核心目录。
     * @param filePath - 待检查的文件路径
     * @returns 如果路径包含 patterns/core/ 则返回 true
     */
    static isCorePath(filePath) {
        return /patterns[/]core[/]/.test(filePath);
    }
}
//# sourceMappingURL=writer.js.map