/**
 * Diff Ownership — 追踪文件变更归属，支持写前 CAS 校验。
 * CAP-011 实验性: 只做状态模型和 CAS 校验，不替换现有写入流程，不新增 hard block。
 *
 * Diff 归属分类:
 * - baseline:    任务开始前已存在的工作区变更
 * - task_owned:  本任务通过合法写入产生的变更
 * - human_external: 任务期间人类或其他进程的修改
 * - unknown:     无法归因的变更，保守处理
 *
 * CAS (Compare-And-Swap) 校验:
 * - 写前比较目标文件当前 hash 与上次记录的 hash
 * - 不一致 → 校验失败，返回冲突信息
 * - 一致或文件不存在 → 校验通过
 */
import fs from "node:fs";
import path from "node:path";
import { createHash } from "node:crypto";
import { debug } from "../core/logger.js";
function sha256Hex(content) {
    return createHash("sha256").update(content).digest("hex");
}
/** 大文件阈值: >50MB 跳过 hash 计算，返回 null */
const LARGE_FILE_THRESHOLD = 50 * 1024 * 1024;
function hashFile(filePath) {
    try {
        const stat = fs.statSync(filePath);
        if (stat.size > LARGE_FILE_THRESHOLD) {
            debug("差异所有权", "文件超过大文件阈值，跳过 hash 计算 —", filePath);
            return null;
        }
        const content = fs.readFileSync(filePath);
        return sha256Hex(content);
    }
    catch {
        return null;
    }
}
/**
 * 捕获一组文件的 baseline 快照。
 * 文件不存在时 content_hash=null，ownership=baseline。
 */
export function captureBaseline(filePaths, taskId, now) {
    const ts = (now ?? new Date()).toISOString();
    debug("差异所有权", "捕获 baseline 快照, 任务=" + taskId + ", 文件数=" + filePaths.length);
    return filePaths.map((fp) => ({
        file_path: path.resolve(fp),
        content_hash: hashFile(fp),
        ownership: "baseline",
        task_id: null,
        captured_at: ts,
    }));
}
/**
 * 写前 CAS 校验: 比较文件当前 hash 与期望 hash。
 * - expected=null 且文件不存在 → ok（新文件）
 * - expected=hash 且文件当前 hash 一致 → ok
 * - 不一致 → 校验失败，推断归属
 */
export function casCheck(filePath, expectedHash, taskId, snapshots) {
    debug("差异所有权", "CAS 校验开始, 文件=" + filePath + ", 任务=" + taskId);
    const resolved = path.resolve(filePath);
    const currentHash = hashFile(resolved);
    // 文件不存在 + 预期不存在 → ok, ownership 取最新 snapshot
    if (expectedHash === null && currentHash === null) {
        debug("差异所有权", "CAS 校验通过 — 文件不存在且预期不存在");
        const snap = findLatestSnapshot(filePath, snapshots);
        return {
            file_path: resolved,
            cas_ok: true,
            current_hash: null,
            expected_hash: null,
            conflict_reason: null,
            inferred_ownership: snap?.ownership ?? "baseline",
        };
    }
    // hash 一致 → ok, ownership 取最新匹配 snapshot 的归属
    if (expectedHash === currentHash) {
        debug("差异所有权", "CAS 校验通过 — 文件 hash 一致");
        const snap = findLatestSnapshot(filePath, snapshots);
        return {
            file_path: resolved,
            cas_ok: true,
            current_hash: currentHash,
            expected_hash: expectedHash,
            conflict_reason: null,
            inferred_ownership: snap?.ownership ?? "baseline",
        };
    }
    // 不一致 → 判断归属（取最近一次快照）
    const snap = findLatestSnapshot(filePath, snapshots);
    let ownership = "unknown";
    let reason;
    if (snap && snap.ownership === "baseline" && snap.content_hash !== currentHash) {
        // 文件在 baseline 之后被修改过，但不是本任务改的
        ownership = "human_external";
        reason = `file changed since baseline (expected ${expectedHash?.substring(0, 12) ?? "null"}, got ${currentHash?.substring(0, 12) ?? "null"})`;
    }
    else if (snap && snap.ownership === "task_owned") {
        // 本任务之前改过，但又变了（可能是外部并发修改）
        ownership = "human_external";
        reason = `task-owned file modified externally (expected ${expectedHash?.substring(0, 12) ?? "null"}, got ${currentHash?.substring(0, 12) ?? "null"})`;
    }
    else {
        ownership = "unknown";
        reason = `unexpected file state (expected ${expectedHash?.substring(0, 12) ?? "null"}, got ${currentHash?.substring(0, 12) ?? "null"})`;
    }
    debug("差异所有权", "CAS 校验失败, 文件=" + filePath + ", 原因=" + reason + ", 归属=" + ownership);
    return {
        file_path: resolved,
        cas_ok: false,
        current_hash: currentHash,
        expected_hash: expectedHash,
        conflict_reason: reason,
        inferred_ownership: ownership,
    };
}
/**
 * 记录一次任务写入，返回新的 snapshot。
 */
export function recordTaskWrite(filePath, taskId, now) {
    debug("差异所有权", "记录任务写入, 文件=" + filePath + ", 任务=" + taskId);
    return {
        file_path: path.resolve(filePath),
        content_hash: hashFile(filePath),
        ownership: "task_owned",
        task_id: taskId,
        captured_at: (now ?? new Date()).toISOString(),
    };
}
/**
 * 根据文件路径查找最新快照（反向遍历，兼容 ES2022）。
 */
export function findLatestSnapshot(filePath, snapshots) {
    debug("差异所有权", "查找最新快照, 文件=" + filePath);
    const resolved = path.resolve(filePath);
    for (let i = snapshots.length - 1; i >= 0; i--) {
        if (snapshots[i].file_path === resolved)
            return snapshots[i];
    }
    return undefined;
}
/**
 * 统计各归属的文件数。
 */
export function summarizeOwnership(snapshots) {
    debug("差异所有权", "统计归属分布, 快照总数=" + snapshots.length);
    const counts = {
        baseline: 0,
        task_owned: 0,
        human_external: 0,
        unknown: 0,
    };
    for (const s of snapshots) {
        counts[s.ownership]++;
    }
    return counts;
}
//# sourceMappingURL=diff_ownership.js.map