/**
 * ============================================================
 *  src/context/archive.ts —— /new 自动归档历史对话
 * ------------------------------------------------------------
 *  做的事：
 *    `/new` 清空上下文前，先把当前对话历史落盘到 archive 目录，
 *    供后续 `/list-archive`、`/restore <id>` 查看 / 找回。
 *
 *  文件路径：
 *    <MINIMAL_AGENT_HOME ?? ~>/.minimal-agent/archive/<cwd-hash>/<ISO-timestamp>.json
 *    - cwd-hash：当前工作目录 sha1 前 12 位（避免不同项目 archive 串台）
 *    - timestamp：new Date().toISOString().replace(/:/g, '-')（Windows 不允许冒号）
 *
 *  存储格式：
 *    { updatedAt: number; messages: Message[] }
 *
 *  抉择：
 *    - 仅含 system 消息（空对话）跳过写入，返回空字符串
 *    - 失败由调用方 try/catch 吞掉，本模块不做 stderr（保持纯函数语义）
 *    - 损坏文件在 listArchives 中静默 skip
 *    - 通过 MINIMAL_AGENT_HOME 环境变量让测试切到临时目录，避免污染真实 ~/.minimal-agent
 * ============================================================
 */
import { createHash } from 'node:crypto';
import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { getWorkingDir } from '../bootstrap/workingDir.js';
/** 解析 archive 根目录，支持 MINIMAL_AGENT_HOME 测试覆盖 */
function agentHome() {
    return process.env.MINIMAL_AGENT_HOME ?? homedir();
}
function archiveDir(cwd) {
    const hash = createHash('sha1').update(cwd).digest('hex').slice(0, 12);
    return join(agentHome(), '.minimal-agent', 'archive', hash);
}
/**
 * 归档当前 messages 到 archive 目录。
 *
 * @returns archive id（成功）或空字符串（无内容跳过）
 *
 * 行为：
 *  - messages 为空 / 仅含 system role → 跳过，返回 ''
 *  - 写入失败抛错（调用方负责吞）
 */
export async function archiveCurrent(messages) {
    const nonSystem = messages.filter((m) => m.role !== 'system');
    if (nonSystem.length === 0)
        return '';
    const dir = archiveDir(getWorkingDir());
    await mkdir(dir, { recursive: true });
    const id = new Date().toISOString().replace(/:/g, '-');
    const path = join(dir, `${id}.json`);
    await writeFile(path, JSON.stringify({ updatedAt: Date.now(), messages }), 'utf8');
    return id;
}
/**
 * 列出当前 cwd 下所有归档，最新优先（按文件 mtime 倒序）。
 *
 * 损坏 / 无法解析的文件会被静默跳过——不影响其它条目展示。
 * 目录不存在视作空列表。
 */
export async function listArchives() {
    const dir = archiveDir(getWorkingDir());
    let names;
    try {
        names = await readdir(dir);
    }
    catch {
        return [];
    }
    const entries = [];
    for (const name of names) {
        if (!name.endsWith('.json'))
            continue;
        const id = name.slice(0, -'.json'.length);
        const fpath = join(dir, name);
        try {
            const st = await stat(fpath);
            const raw = await readFile(fpath, 'utf8');
            const data = JSON.parse(raw);
            if (!Array.isArray(data.messages))
                continue;
            const firstUser = data.messages.find((m) => m.role === 'user');
            const preview = firstUser && typeof firstUser.content === 'string'
                ? firstUser.content.replace(/\s+/g, ' ').slice(0, 80)
                : '';
            entries.push({
                id,
                updatedAt: st.mtimeMs,
                msgCount: data.messages.length,
                firstUserPreview: preview,
            });
        }
        catch {
            // 损坏文件静默 skip
        }
    }
    return entries.sort((a, b) => b.updatedAt - a.updatedAt);
}
/**
 * 按 id 恢复归档。
 *
 * @returns messages 数组（成功）；找不到 / 损坏 / messages 非数组 → null
 */
export async function restoreArchive(id) {
    const dir = archiveDir(getWorkingDir());
    const path = join(dir, `${id}.json`);
    try {
        const raw = await readFile(path, 'utf8');
        const data = JSON.parse(raw);
        return Array.isArray(data.messages) ? data.messages : null;
    }
    catch {
        return null;
    }
}
