// EternalAI 客户端 — 用 sync_token 回调 EternalAI 拉取 SOUL.md 和 config.yaml
// P0 修复：使用环境变量 ETERNALAI_BASE_URL，不信任请求体中的 filePullBaseUrl

const FETCH_TIMEOUT_MS = 15000;
const MAX_FILE_SIZE = 1024 * 1024; // 1MB 上限

// P0 修复：从环境变量获取 EternalAI 地址，防止 SSRF + sync_token 泄露
const ETERNALAI_BASE_URL = process.env.ETERNALAI_BASE_URL || '';

// 拉取单个文件，返回文本内容
async function pullFile(baseUrl, roleId, filename, syncToken) {
  const url = `${baseUrl}/api/hermes/roles/${roleId}/${filename}`;
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);

  try {
    const response = await fetch(url, {
      headers: { 'X-Sync-Token': syncToken },
      signal: controller.signal,
    });

    if (!response.ok) {
      throw new Error(`拉取 ${filename} 失败: HTTP ${response.status}`);
    }

    // P1 修复：校验文件大小，防止 OOM
    const contentLength = parseInt(response.headers.get('content-length') || '0', 10);
    if (contentLength > MAX_FILE_SIZE) {
      throw new Error(`${filename} 文件过大 (${contentLength} bytes)，超过 ${MAX_FILE_SIZE} 上限`);
    }

    const text = await response.text();
    // 即使没有 Content-Length 也校验实际大小
    if (text.length > MAX_FILE_SIZE) {
      throw new Error(`${filename} 文件过大 (${text.length} chars)，超过上限`);
    }

    return text;
  } catch (err) {
    if (err.name === 'AbortError') {
      throw new Error(`拉取 ${filename} 超时（${FETCH_TIMEOUT_MS}ms）`);
    }
    throw err;
  } finally {
    clearTimeout(timeout);
  }
}

// 拉取角色的 SOUL.md 和 config.yaml
// P0 修复：使用 ETERNALALAI_BASE_URL 环境变量，忽略请求体中的 filePullBaseUrl
async function pullRoleFiles(roleId, syncToken) {
  const baseUrl = ETERNALAI_BASE_URL.replace(/\/+$/, ''); // 去掉尾部斜杠
  if (!baseUrl) {
    throw new Error('ETERNALAI_BASE_URL 环境变量未配置，无法拉取文件');
  }

  const [soulMd, configYaml] = await Promise.all([
    pullFile(baseUrl, roleId, 'SOUL.md', syncToken),
    pullFile(baseUrl, roleId, 'config.yaml', syncToken),
  ]);

  return { soulMd, configYaml };
}

module.exports = { pullRoleFiles, pullFile, ETERNALAI_BASE_URL };
