/**
 * 模板初始化同步 — init 命令的知识模板复制与全局模式安装。
 *
 * 将 CLI copyKnowledgeTemplates / copyGlobalPatterns 的核心逻辑抽为生产模块，
 * 由 CLI、release gate、observed_consumption 共同调用。
 * 每次调用均记录 consumption_trace，保证 trace 来源真实一致。
 * 同步完成后写入 sync-manifest.json，记录每个同步文件的来源身份元数据。
 * 管线模板同步完成后写入 manifest.yaml，记录系统类型和文件修改状态。
 */
import path from "node:path";
import fs from "node:fs";
import crypto from "node:crypto";
import { fileURLToPath } from "node:url";
import YAML from "yaml";
import { getContractDecision } from "./template_asset_contract_registry.js";
import { recordConsumptionTrace } from "./consumption_trace_store.js";
import { safeJsonParse } from "../core/helpers.js";
const MANIFEST_PATH = ".soloforge/sync-manifest.json";
/**
 * 读取同步清单。
 */
export function readSyncManifest(projectPath) {
    const manifestPath = path.join(projectPath, MANIFEST_PATH);
    if (!fs.existsSync(manifestPath))
        return null;
    return safeJsonParse(fs.readFileSync(manifestPath, "utf-8")) ?? null;
}
/**
 * 写入同步清单。
 */
function writeSyncManifest(projectPath, manifest) {
    const manifestPath = path.join(projectPath, MANIFEST_PATH);
    fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
    fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
}
/** 计算文件 body SHA256（排除健康检查会修改的字段：frontmatter 和 YAML status/updated_at） */
function fileHash(filePath) {
    const content = fs.readFileSync(filePath, "utf-8");
    if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
        // YAML 文件: 移除 status 和 updated_at 行后哈希
        const stripped = content.split("\n")
            .filter(line => !/^(status|updated_at)\s*:/.test(line.trim()))
            .join("\n");
        return crypto.createHash("sha256").update(stripped).digest("hex").substring(0, 16);
    }
    // Markdown: 排除 frontmatter 后哈希 body
    const bodyStart = content.indexOf("---", content.indexOf("---") + 3);
    const body = bodyStart >= 0 ? content.substring(bodyStart + 3).trimStart() : content;
    return crypto.createHash("sha256").update(body).digest("hex").substring(0, 16);
}
/**
 * 从权威来源（当前安装的模板包）计算 source_contract_path 的 body hash。
 * 用于替代 sync-manifest.json 中用户可编辑的 source_hash。
 * 返回 null 表示权威来源文件不存在（用户可能已卸载/升级包）。
 */
export function computeAuthoritativeHash(sourceContractPath) {
    const thisDir = path.dirname(fileURLToPath(import.meta.url));
    // src/engine/templates/ → ../../../ → 项目根
    const absPath = path.resolve(thisDir, "..", "..", "..", sourceContractPath);
    if (!fs.existsSync(absPath))
        return null;
    return fileHash(absPath);
}
/**
 * 按目标 knowledge relativePath 反向解析权威来源身份。
 * 不依赖用户可编辑 manifest 中的 source_contract_path/source_asset_id。
 * 返回 null 表示无对应权威映射（非同步资产或模板包缺失）。
 */
export function resolveAuthoritativeIdentity(knowledgeRelPath) {
    // knowledgeRelPath 来自 .soloforge/knowledge/ 下的相对路径
    // 新结构下直接映射到 templates/{knowledgeRelPath}
    const contractPath = `templates/${knowledgeRelPath}`;
    const hash = computeAuthoritativeHash(contractPath);
    if (hash === null)
        return null;
    // getContractDecision 同步可用（已由 import 引入）
    // 此函数由 KIM 在 filterByContractGate 中调用，KIM 已 import getContractDecision
    // 直接返回路径和 hash，让 KIM 通过 getContractDecision 获取合同
    return {
        source_contract_path: contractPath,
        source_asset_id: "", // 由 KIM 通过 getContractDecision 填充
        source_hash: hash,
    };
}
/**
 * 遍历 templates/knowledge 下所有文件，通过 getContractDecision 门禁，
 * 记录 consumption_trace 后复制到用户项目 .soloforge/knowledge/ 目录。
 * 同步完成后写入 sync-manifest.json 记录来源身份。
 */
export async function copyKnowledgeToProject(templatesRootDir, projectPath) {
    const result = {
        copied: 0,
        updated: 0,
        skipped_internal: 0,
        skipped_no_contract: 0,
        skipped_up_to_date: 0,
        skipped_user_modified: 0,
        patterns_copied: 0,
        patterns_skipped: 0,
    };
    // 新结构: 按类型目录扫描
    const knowledgeScanDirs = [
        "internal/rules",
        "procedures",
        "checklists",
        "domain",
        "internal/patterns",
        "profiles",
    ];
    // 读取现有清单或创建新清单
    const manifest = readSyncManifest(projectPath) ?? { version: 1, entries: {} };
    for (const subDir of knowledgeScanDirs) {
        const knowledgeTemplatesDir = path.join(templatesRootDir, "templates", subDir);
        if (!fs.existsSync(knowledgeTemplatesDir))
            continue;
        const stack = [knowledgeTemplatesDir];
        while (stack.length > 0) {
            const currentDir = stack.pop();
            for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
                const srcPath = path.join(currentDir, entry.name);
                if (entry.isDirectory()) {
                    stack.push(srcPath);
                    continue;
                }
                if (!/\.(md|yaml|yml)$/.test(entry.name))
                    continue;
                const relFromSubDir = path.relative(knowledgeTemplatesDir, srcPath).replace(/\\/g, "/");
                const fullRelPath = `templates/${subDir}/${relFromSubDir}`;
                const destRelPath = `${subDir}/${relFromSubDir}`;
                const destPath = path.join(projectPath, ".soloforge", "knowledge", destRelPath);
                const decision = getContractDecision(fullRelPath);
                if (decision.decision === "unregistered") {
                    recordConsumptionTrace({
                        consumer: "init", asset_id: fullRelPath, asset_path: fullRelPath,
                        contract_decision: "unregistered",
                        fields_consumed: ["asset_path", "contract_decision", "target_path"],
                        matched_reason: "无合同注册",
                        task_id_or_route: "copyKnowledgeTemplates",
                        consumed_at: new Date().toISOString(),
                    });
                    result.skipped_no_contract++;
                    continue;
                }
                if (decision.decision === "denied") {
                    recordConsumptionTrace({
                        consumer: "init", asset_id: decision.contract?.asset_id ?? fullRelPath,
                        asset_path: fullRelPath, contract_decision: "denied",
                        fields_consumed: ["asset_path", "contract_decision", "target_path"],
                        matched_reason: `内部资产: ${decision.contract?.asset_visibility}`,
                        task_id_or_route: "copyKnowledgeTemplates",
                        consumed_at: new Date().toISOString(),
                    });
                    result.skipped_internal++;
                    continue;
                }
                if (decision.contract && !decision.contract.sync_to_user_project) {
                    recordConsumptionTrace({
                        consumer: "init", asset_id: decision.contract.asset_id,
                        asset_path: fullRelPath, contract_decision: "denied",
                        fields_consumed: ["asset_path", "contract_decision", "target_path"],
                        matched_reason: "sync_to_user_project=false",
                        task_id_or_route: "copyKnowledgeTemplates",
                        consumed_at: new Date().toISOString(),
                    });
                    result.skipped_internal++;
                    continue;
                }
                const contractAssetId = decision.contract?.asset_id ?? fullRelPath;
                const contractMainlineConsumer = decision.contract?.mainline_consumer;
                const newSourceHash = fileHash(srcPath);
                if (!fs.existsSync(destPath)) {
                    // 新文件：直接复制
                    fs.mkdirSync(path.dirname(destPath), { recursive: true });
                    fs.copyFileSync(srcPath, destPath);
                    manifest.entries[destRelPath] = {
                        source_asset_id: contractAssetId,
                        source_contract_path: fullRelPath,
                        source_hash: newSourceHash,
                        synced_at: new Date().toISOString(),
                    };
                    recordConsumptionTrace({
                        consumer: "init", asset_id: contractAssetId,
                        asset_path: fullRelPath, contract_decision: "allowed",
                        fields_consumed: ["asset_path", "contract_decision", "target_path"],
                        matched_reason: "新增复制到用户项目",
                        task_id_or_route: "copyKnowledgeTemplates",
                        consumed_at: new Date().toISOString(),
                        declared_consumer: contractMainlineConsumer,
                    });
                    result.copied++;
                }
                else {
                    // 已有文件：检测是否过时
                    const existingEntry = manifest.entries[destRelPath];
                    const diskHash = fileHash(destPath);
                    if (existingEntry && existingEntry.source_hash === diskHash) {
                        // 用户未修改：磁盘 hash == 上次同步的源 hash
                        if (existingEntry.source_hash !== newSourceHash) {
                            // 源已更新 → 自动更新副本
                            fs.copyFileSync(srcPath, destPath);
                            manifest.entries[destRelPath] = {
                                source_asset_id: contractAssetId,
                                source_contract_path: fullRelPath,
                                source_hash: newSourceHash,
                                synced_at: new Date().toISOString(),
                            };
                            recordConsumptionTrace({
                                consumer: "init", asset_id: contractAssetId,
                                asset_path: fullRelPath, contract_decision: "allowed",
                                fields_consumed: ["asset_path", "contract_decision", "target_path"],
                                matched_reason: "过时副本自动更新",
                                task_id_or_route: "copyKnowledgeTemplates",
                                consumed_at: new Date().toISOString(),
                                declared_consumer: contractMainlineConsumer,
                            });
                            result.updated++;
                        }
                        else {
                            // 已是最新 → 跳过，仅刷新 synced_at
                            manifest.entries[destRelPath] = {
                                ...existingEntry,
                                synced_at: new Date().toISOString(),
                            };
                            result.skipped_up_to_date++;
                        }
                    }
                    else {
                        // 用户已修改（磁盘 hash ≠ 上次同步的源 hash）→ 不覆盖，保留旧 manifest 条目
                        result.skipped_user_modified++;
                        recordConsumptionTrace({
                            consumer: "init", asset_id: contractAssetId,
                            asset_path: fullRelPath, contract_decision: "denied",
                            fields_consumed: ["asset_path", "contract_decision", "target_path"],
                            matched_reason: "用户已修改知识副本，跳过更新",
                            task_id_or_route: "copyKnowledgeTemplates",
                            consumed_at: new Date().toISOString(),
                            declared_consumer: contractMainlineConsumer,
                        });
                    }
                }
            }
        }
    }
    writeSyncManifest(projectPath, manifest);
    return result;
}
/**
 * 安装全局模式到指定目录。
 * 遍历 templates/patterns 下所有文件，记录 consumption_trace。
 */
export async function copyPatternsToGlobal(templatesRootDir, patternsDestDir) {
    let copied = 0;
    let skipped = 0;
    const templatesDir = path.join(templatesRootDir, "templates", "patterns");
    if (!fs.existsSync(templatesDir))
        return { copied: 0, skipped: 0 };
    fs.mkdirSync(patternsDestDir, { recursive: true });
    const files = fs.readdirSync(templatesDir).filter(f => f.endsWith(".md"));
    for (const f of files) {
        const src = path.join(templatesDir, f);
        const dest = path.join(patternsDestDir, f);
        const fullRelPath = `templates/patterns/${f}`;
        const decision = getContractDecision(fullRelPath);
        recordConsumptionTrace({
            consumer: "init",
            asset_id: decision.contract?.asset_id ?? fullRelPath,
            asset_path: fullRelPath,
            contract_decision: decision.decision === "allowed" ? "allowed" : decision.decision === "denied" ? "denied" : "unregistered",
            fields_consumed: ["asset_path", "contract_decision", "target_path"],
            matched_reason: decision.decision === "allowed" ? "全局模式可复制" : `${decision.decision}`,
            task_id_or_route: "copyGlobalPatterns",
            consumed_at: new Date().toISOString(),
        });
        if (decision.decision === "allowed") {
            if (!fs.existsSync(dest)) {
                fs.copyFileSync(src, dest);
                copied++;
            }
            else {
                skipped++;
            }
        }
    }
    return { copied, skipped };
}
const PIPELINE_MANIFEST_PATH = ".soloforge/templates/manifest.yaml";
/** 读取管线模板清单 */
export function readPipelineManifest(projectPath) {
    const manifestPath = path.join(projectPath, PIPELINE_MANIFEST_PATH);
    if (!fs.existsSync(manifestPath))
        return null;
    try {
        const raw = fs.readFileSync(manifestPath, "utf-8");
        const parsed = YAML.parse(raw);
        if (parsed && parsed.version === 2 && parsed.files)
            return parsed;
        return null;
    }
    catch {
        return null;
    }
}
/** 写入管线模板清单 */
function writePipelineManifest(projectPath, manifest) {
    const manifestPath = path.join(projectPath, PIPELINE_MANIFEST_PATH);
    fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
    fs.writeFileSync(manifestPath, YAML.stringify(manifest), "utf-8");
}
/**
 * 复制管线模板到用户项目。
 * 根据 systemType 决定复制 shared/ + (new-system/ 或 existing-system/)。
 * 已修改文件永不覆盖，提示用户手动合并。
 */
export async function copyPipelineTemplatesToProject(templatesRootDir, projectPath, systemType) {
    const result = {
        copied: 0,
        updated: 0,
        skipped_modified: 0,
        skipped_exists: 0,
    };
    // 新结构: 类型优先 — 对每个类型目录，遍历 shared + 系统类型子目录
    const typeDirs = ["artifacts", "rules", "gates"];
    const scopeDirs = ["shared"];
    if (systemType === "新系统") {
        scopeDirs.push("new-system");
    }
    else if (systemType === "老系统") {
        scopeDirs.push("existing-system");
    }
    else {
        // 未指定类型：复制全部
        scopeDirs.push("new-system", "existing-system");
    }
    // 读取版本号
    const pkgPath = path.join(templatesRootDir, "package.json");
    let soloforgeVersion = "0.0.0";
    try {
        const pkg = safeJsonParse(fs.readFileSync(pkgPath, "utf-8"));
        soloforgeVersion = pkg?.version ?? "0.0.0";
    }
    catch { /* 忽略 */ }
    const manifest = readPipelineManifest(projectPath) ?? {
        version: 2,
        system_type: systemType ?? "全部",
        soloforge_version: soloforgeVersion,
        synced_at: new Date().toISOString(),
        files: {},
    };
    // 更新元数据
    manifest.system_type = systemType ?? "全部";
    manifest.soloforge_version = soloforgeVersion;
    manifest.synced_at = new Date().toISOString();
    for (const typeDir of typeDirs) {
        for (const scopeDir of scopeDirs) {
            const srcDir = path.join(templatesRootDir, "templates", typeDir, scopeDir);
            if (!fs.existsSync(srcDir))
                continue;
            const stack = [srcDir];
            while (stack.length > 0) {
                const currentDir = stack.pop();
                for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
                    const srcPath = path.join(currentDir, entry.name);
                    if (entry.isDirectory()) {
                        stack.push(srcPath);
                        continue;
                    }
                    if (!/\.(md|yaml|yml|json)$/.test(entry.name))
                        continue;
                    const relFromScopeDir = path.relative(srcDir, srcPath).replace(/\\/g, "/");
                    const manifestKey = `${typeDir}/${scopeDir}/${relFromScopeDir}`;
                    const destPath = path.join(projectPath, ".soloforge", "templates", typeDir, scopeDir, relFromScopeDir);
                    const currentHash = fileHash(srcPath);
                    const existing = manifest.files[manifestKey];
                    if (fs.existsSync(destPath)) {
                        if (existing?.modified) {
                            // 已标记为用户修改过，永不覆盖
                            recordConsumptionTrace({
                                consumer: "init",
                                asset_id: manifestKey,
                                asset_path: `templates/${manifestKey}`,
                                contract_decision: "allowed",
                                fields_consumed: ["asset_path", "contract_decision", "target_path"],
                                matched_reason: "用户已修改，跳过覆盖",
                                task_id_or_route: "copyPipelineTemplatesToProject",
                                consumed_at: new Date().toISOString(),
                            });
                            result.skipped_modified++;
                            continue;
                        }
                        // 检查磁盘文件是否被用户修改（对比原始 hash）
                        const diskHash = fileHash(destPath);
                        if (existing && existing.source_hash !== diskHash) {
                            // 文件内容与原始 hash 不一致，标记为已修改
                            manifest.files[manifestKey] = { modified: true, source_hash: existing.source_hash };
                            recordConsumptionTrace({
                                consumer: "init",
                                asset_id: manifestKey,
                                asset_path: `templates/${manifestKey}`,
                                contract_decision: "allowed",
                                fields_consumed: ["asset_path", "contract_decision", "target_path"],
                                matched_reason: "磁盘 hash 与原始不一致，标记已修改",
                                task_id_or_route: "copyPipelineTemplatesToProject",
                                consumed_at: new Date().toISOString(),
                            });
                            result.skipped_modified++;
                            continue;
                        }
                        // 文件未修改，更新到最新版
                        fs.copyFileSync(srcPath, destPath);
                        manifest.files[manifestKey] = { modified: false, source_hash: currentHash };
                        recordConsumptionTrace({
                            consumer: "init",
                            asset_id: manifestKey,
                            asset_path: `templates/${manifestKey}`,
                            contract_decision: "allowed",
                            fields_consumed: ["asset_path", "contract_decision", "target_path"],
                            matched_reason: "管线模板更新到最新版",
                            task_id_or_route: "copyPipelineTemplatesToProject",
                            consumed_at: new Date().toISOString(),
                        });
                        result.updated++;
                    }
                    else {
                        // 新文件，直接复制
                        fs.mkdirSync(path.dirname(destPath), { recursive: true });
                        fs.copyFileSync(srcPath, destPath);
                        manifest.files[manifestKey] = { modified: false, source_hash: currentHash };
                        recordConsumptionTrace({
                            consumer: "init",
                            asset_id: manifestKey,
                            asset_path: `templates/${manifestKey}`,
                            contract_decision: "allowed",
                            fields_consumed: ["asset_path", "contract_decision", "target_path"],
                            matched_reason: "管线模板复制到用户项目",
                            task_id_or_route: "copyPipelineTemplatesToProject",
                            consumed_at: new Date().toISOString(),
                        });
                        result.copied++;
                    }
                }
            }
        }
    }
    writePipelineManifest(projectPath, manifest);
    return result;
}
//# sourceMappingURL=template_init_sync.js.map