/**
 * ============================================================
 *  src/config/configFile.ts —— 模式 B 向导写的 provider 配置
 * ------------------------------------------------------------
 *  与 src/config.ts 的关系：
 *    - config.ts 负责"读取 process.env 拼装 Provider"（模式 A）
 *    - 本文件负责"读写 ~/.minimal-agent/config.json"（模式 B）
 *    - 上层（loadProviderLayered）先看 env、再看本文件
 *
 *  与 src/context/persistContext.ts 的关系：
 *    - 复用同一个目录 ~/.minimal-agent/
 *    - 同样"解析失败返回 null、写失败静默"模式
 *
 *  为什么用 JSON 而非 .env 格式：
 *    - 解析无歧义（dotenv 各家行为略有差异）
 *    - 显式区分"向导写的" vs "用户写的 .env"，避免互相覆盖
 *    - JSON 还可以放结构化扩展（如 savedAt 用于排查）
 * ============================================================
 */
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { dirname, join } from 'node:path';
import { execSiblingDir } from '../utils/greenRoot.js';
/**
 * 配置文件的**写**路径（带环境变量覆盖，主要给测试用）。
 * `saveConfig()` / 首次配置向导只写这里——env override 或 home，**绝不写 exe 同级**
 * （U 盘可能只读，且不该污染产品目录）。读路径见 readSavedConfig 的分层。
 */
export function getConfigFilePath() {
    return process.env.MINIMAL_AGENT_CONFIG_FILE ?? homeConfigPath();
}
/** home 默认配置路径（读分层的最后一档；也是 getConfigFilePath 的默认写入位置）。 */
function homeConfigPath() {
    return join(homedir(), '.minimal-agent', 'config.json');
}
/**
 * 解析单个 config.json：不存在 / JSON 损坏 / 必填字段缺失 → null；成功 → SavedConfig。
 */
async function parseConfigFile(file) {
    try {
        const raw = await readFile(file, 'utf8');
        const data = JSON.parse(raw);
        if (typeof data.baseURL !== 'string' ||
            typeof data.apiKey !== 'string' ||
            typeof data.model !== 'string' ||
            data.baseURL.length === 0 ||
            data.apiKey.length === 0 ||
            data.model.length === 0) {
            return null;
        }
        return {
            baseURL: data.baseURL,
            apiKey: data.apiKey,
            model: data.model,
            provider: typeof data.provider === 'string' ? data.provider : undefined,
            contextWindow: typeof data.contextWindow === 'number' && data.contextWindow > 0
                ? data.contextWindow
                : undefined,
            compactRatio: typeof data.compactRatio === 'number' &&
                data.compactRatio > 0 &&
                data.compactRatio <= 1
                ? data.compactRatio
                : undefined,
            tavilyApiKey: typeof data.tavilyApiKey === 'string' && data.tavilyApiKey.length > 0
                ? data.tavilyApiKey
                : undefined,
            savedAt: typeof data.savedAt === 'number' ? data.savedAt : 0,
        };
    }
    catch {
        return null;
    }
}
/**
 * 读取已保存的 provider 配置（**分层**，按优先级返回第一个有效的）：
 *   1. `MINIMAL_AGENT_CONFIG_FILE` 显式覆盖 —— **只读这一个，不 fallthrough**
 *      （保持既有语义 + 测试隔离）
 *   2. exe 同级 `config.json` —— **绿色版 / U 盘的卖家预置配置**（compile 后
 *      `execSiblingDir()` = 真实 exe 目录；dev/npm 下是 node/bun 二进制目录、无此文件 → 跳过）
 *   3. `~/.minimal-agent/config.json` —— 首次配置向导写出的（原行为）
 *
 * 这层让卖家把配好的 config.json 放进绿色目录即完成预配置，客户端零配置开箱即用
 * （loadProviderLayered 拿到 provider → 不进向导、`-p` 不报 config_error）。
 *
 * 加性保证：dev / npm 模式无 override 时，exe 同级无 config.json → 只命中 home → 行为不变。
 */
export async function readSavedConfig() {
    const override = process.env.MINIMAL_AGENT_CONFIG_FILE;
    if (override)
        return parseConfigFile(override);
    for (const file of [join(execSiblingDir(), 'config.json'), homeConfigPath()]) {
        const cfg = await parseConfigFile(file);
        if (cfg)
            return cfg;
    }
    return null;
}
/**
 * 写入向导收集到的配置。
 * unix 上 chmod 600（仅本人可读写），Windows 上 chmod 是 no-op，靠 NTFS ACL。
 */
export async function saveConfig(cfg) {
    const file = getConfigFilePath();
    await mkdir(dirname(file), { recursive: true });
    const data = { ...cfg, savedAt: Date.now() };
    await writeFile(file, JSON.stringify(data, null, 2), 'utf8');
    try {
        await chmod(file, 0o600);
    }
    catch {
    }
}
