/**
 * ============================================================
 *  src/tools/shared/fileState.ts —— Pre-read Guard 状态表
 * ------------------------------------------------------------
 *  对应上游 D:\code\kakadeai\utils\fileStateCache.ts:4-14
 *  + D:\code\kakadeai\tools\FileWriteTool\FileWriteTool.ts:198-219 的"先读后写"约束。
 *
 *  纯 module-level Map，记录每个文件最近一次被 Read 时的 (mtime, size)。
 *  Write/Edit 在落盘前调用 assertFresh：
 *    1. 没有记录 + 文件已存在 → 报错"请先 Read"
 *    2. 没有记录 + 文件不存在 → 放行（新建场景）
 *    3. 当前 mtime > 记录的 mtime → 报错"文件被外部修改"
 *
 *  /new 调用 clearContext 时连带调 clearFileState() 防残留。
 *
 *  v2 增量（防"读后空转"）：recordRead 额外记录最近一次读取的 (offset, limit)
 *  与"同区连续重读次数"，并返回该次数。Read 工具据此在"同一区间 + 文件未变 +
 *  第二次起"时给模型一行"别重复读、直接用已读内容"的提示（仅提示，不拦截）。
 *  文件一旦被 Edit/Write 或外部改动（mtime 漂移），重读即合法、计数归 1。
 * ============================================================
 */
import { existsSync, statSync } from 'node:fs';
const fileState = new Map();
/**
 * 记录一次 Read，返回本次是"同区重读"的第几次（首次=1）。
 *
 * "同区重读" = 文件 mtime 未漂移（内容没变） + 这次 (offset,limit) 与上次完全相同。
 * 调用方（Read 工具）据返回值 >= 2 判定模型在空转、给提示。
 * 文件被 Edit/Write 或外部改动（mtime 漂移）即视为合法重读，计数归 1、不提示。
 */
export function recordRead(absPath, opts) {
    try {
        const st = statSync(absPath);
        const prev = fileState.get(absPath);
        const sameRegionUnchanged = prev !== undefined &&
            st.mtimeMs <= prev.timestamp &&
            prev.offset === opts?.offset &&
            prev.limit === opts?.limit;
        const reads = sameRegionUnchanged ? (prev.reads ?? 1) + 1 : 1;
        fileState.set(absPath, {
            timestamp: st.mtimeMs,
            size: st.size,
            truncated: opts?.truncated ?? false,
            offset: opts?.offset,
            limit: opts?.limit,
            reads,
        });
        return reads;
    }
    catch {
        // 文件不存在或无权访问 —— 不记录，让 Read 自己处理错误
        return 1;
    }
}
/** 查询上次 Read 是否被截断。供 Edit/MultiEdit 给出更精准 error hint、Write 做 hard guard 用。 */
export function wasTruncated(absPath) {
    return fileState.get(absPath)?.truncated ?? false;
}
export function assertFresh(absPath) {
    const entry = fileState.get(absPath);
    if (!entry) {
        if (existsSync(absPath)) {
            return {
                ok: false,
                reason: 'never-read',
                error: `文件 ${absPath} 已存在但未在本会话 Read 过。请先用 Read 工具读取，确认内容后再修改。`,
            };
        }
        // 文件不存在 → 新建场景，放行
        return { ok: true };
    }
    try {
        const st = statSync(absPath);
        if (st.mtimeMs > entry.timestamp) {
            return {
                ok: false,
                reason: 'mtime-drift',
                error: `${absPath} 在 Read 后被外部修改（mtime 漂移）。请重新用 Read 工具读取最新内容。`,
            };
        }
    }
    catch {
        // 记录存在但文件没了 —— 不阻拦，允许重新创建
        return { ok: true };
    }
    return { ok: true };
}
export function clearFileState() {
    fileState.clear();
}
// 测试辅助：只在测试里用，业务代码不要碰
export function _getFileStateSize() {
    return fileState.size;
}
