/**
 * Tool Guard — LLM 审查模块
 *
 * 当正则规则无法确定安全性时（返回 UNCERTAIN），调用 LLM 进行兜底审查。
 * 复用 server/services/llm.js 的 chatCompletion()，使用 OpenAI 兼容格式。
 */

import { chatCompletion } from '../llm.js';

const MODEL = process.env.TOOL_GUARD_LLM_MODEL || 'gemini-3.1-flash-lite-preview';
const TIMEOUT_MS = parseInt(process.env.TOOL_GUARD_LLM_TIMEOUT_MS || '5000', 10);

/**
 * 使用 LLM 审查工具调用安全性
 * @param {string} toolName
 * @param {Record<string, unknown>} input
 * @param {object} context - { userUuid, allowedPrefixes, cwd, tempDirs }
 * @param {string[]} uncertainReasons - 触发 LLM 审查的原因
 * @returns {Promise<{ allowed: boolean, reason?: string }>}
 */
export async function reviewWithLLM(toolName, input, context, uncertainReasons) {
  const { userUuid, allowedPrefixes, cwd } = context;

  // 如果 OPENAI_API_KEY 未配置，降级为放行
  if (!process.env.OPENAI_API_KEY) {
    console.warn('[ToolGuard] OPENAI_API_KEY 未配置，LLM 审查降级为放行');
    return { allowed: true };
  }

  const systemPrompt = buildSystemPrompt(userUuid, allowedPrefixes, cwd);
  const userPrompt = buildUserPrompt(toolName, input, uncertainReasons);

  try {
    // 带超时的 LLM 调用
    const result = await Promise.race([
      chatCompletion({
        systemPrompt,
        userPrompt,
        model: MODEL,
        maxTokens: 256,
        temperature: 0,
      }),
      new Promise((_, reject) =>
        setTimeout(() => reject(new Error('LLM 审查超时')), TIMEOUT_MS)
      ),
    ]);

    return parseResponse(result);
  } catch (err) {
    if (err.message === 'LLM 审查超时') {
      return { allowed: false, reason: 'LLM 审查超时，出于安全考虑已拒绝' };
    }
    throw err; // 其他错误向上抛出，由 index.js 的 catch 处理
  }
}

/**
 * 构建系统提示词
 */
function buildSystemPrompt(userUuid, allowedPrefixes, cwd) {
  return `你是一个多用户 AI 编程平台的安全审查员。你需要判断一个 AI 助手的工具调用是否安全。

## 用户上下文
- 用户 ID: ${userUuid}
- 用户允许的目录:
  - ${allowedPrefixes[0]}
  - ${allowedPrefixes[1]}
- 当前工作目录: ${cwd || '未指定'}
- 临时目录要求: 必须使用 /tmp/${userUuid}/ 子文件夹

## 安全规则
1. 用户只能访问自己目录下的文件，不能访问其他用户的文件或系统文件
2. 临时目录（/tmp/、/var/tmp/）必须使用用户 ID 子文件夹（/tmp/${userUuid}/）
3. 不允许修改系统配置（nginx、docker、systemd、防火墙等）
4. 不允许执行危险命令（sudo、kill 系统进程、rm -rf /、shutdown 等）
5. 不允许全局安装包（npm -g、pip 非虚拟环境、pip --user 等）；pip 必须在虚拟环境内执行（venv/bin/pip 或 .venv/bin/pip）
6. 允许的操作：运行用户自己项目（node/npm/python）、本地安装依赖、git 操作、创建虚拟环境

## 判断原则
- 如果操作明显安全（在用户目录内操作、本地安装依赖等），判定为 safe
- 如果操作有潜在风险但无法确定，倾向于判定为 unsafe（宁可误拒不可漏放）
- 判定为 unsafe 时，在 reason 中给出简短的中文说明，帮助用户理解被拒绝的原因

## 响应格式
仅输出 JSON，不要输出其他任何内容：
{"safe": true, "reason": "安全操作说明"}
或
{"safe": false, "reason": "拒绝原因说明"}`;
}

/**
 * 构建用户提示词
 */
function buildUserPrompt(toolName, input, uncertainReasons) {
  const inputStr = typeof input === 'object' ? JSON.stringify(input, null, 2) : String(input);
  return `## 待审查的工具调用
- 工具名称: ${toolName}
- 工具参数:
${inputStr}

## 需要额外关注的原因
${uncertainReasons.map(r => `- ${r}`).join('\n')}`;
}

/**
 * 解析 LLM 响应
 */
function parseResponse(responseText) {
  try {
    // 尝试提取 JSON（LLM 可能返回 markdown 包裹的 JSON）
    const jsonMatch = responseText.match(/\{[\s\S]*\}/);
    if (!jsonMatch) {
      return { allowed: false, reason: 'LLM 审查返回格式异常，出于安全考虑已拒绝' };
    }

    const parsed = JSON.parse(jsonMatch[0]);

    if (typeof parsed.safe !== 'boolean') {
      return { allowed: false, reason: 'LLM 审查返回格式异常，出于安全考虑已拒绝' };
    }

    return parsed.safe
      ? { allowed: true }
      : { allowed: false, reason: parsed.reason || 'LLM 审查判定为不安全操作' };
  } catch {
    return { allowed: false, reason: 'LLM 审查响应解析失败，出于安全考虑已拒绝' };
  }
}
