/**
 * 工具结果显示层裁剪 — 防止 base64 大字段撑爆渲染进程内存
 *
 * 背景：view_image 等工具返回的 dataUrl（最大 ~6.7MB/张 base64）会经 IPC 流式事件
 * 完整进入渲染进程，并写入 store 的 message / traceStep。若直接 JSON.stringify 渲染，
 * 单条 tool_result 就会把 ~6.7MB 字符串塞进 DOM，多轮多图累积导致渲染进程 OOM 崩溃。
 *
 * 策略：仅在最外层显示边界剥离 dataUrl 这类超大 base64 字段，返回轻量摘要。
 * - 主进程 / DB / LLM 链路保留完整 dataUrl（多模态 LLM 看图依赖它，不可裁剪）
 * - 渲染进程只用于"展示"的 result 先过 toDisplayResult，store 内原始 result 不变
 *
 * 副作用：纯函数，不修改入参（返回新对象或原值引用）。
 */

/** 视为"超大"的字符串阈值（字符数），超过即判定为需裁剪的大字段 */
const LARGE_FIELD_THRESHOLD = 8_192;

/**
 * 判断一个值是否为"含大 base64 的对象"——即 view_image 等工具的返回结构。
 * 仅当对象包含 dataUrl（或同等量级的 base64）字段时才裁剪，普通结构原样返回。
 */
function hasLargeBase64(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
    && 'dataUrl' in value;
}

/**
 * 将工具结果转换为"适合显示"的轻量版本：剥离 dataUrl 等超大字段。
 *
 * @param result 原始工具结果（来自 store 的 message.toolCalls[].result）
 * @returns 用于 UI 展示的裁剪后结果（原值或新对象），永不修改入参
 */
export function toDisplayResult(result: unknown): unknown {
  // 非对象（string / number / null 等）无需裁剪
  if (typeof result !== 'object' || result === null) {
    return result;
  }

  // 含 dataUrl 的结果（view_image 等）：剥离 dataUrl，保留 path/size/mimeType 等小字段
  if (hasLargeBase64(result)) {
    const { dataUrl: _omit, ...rest } = result as Record<string, unknown>;
    void _omit; // 显式标记丢弃
    return {
      ...rest,
      // 标注被裁剪，便于用户/开发者理解为何看不到完整 dataUrl
      _displayNote: '[image base64 omitted for display — visible to LLM only]',
    };
  }

  // 其余对象/数组：递归剔除任意超长字符串字段（防御性，覆盖未来可能的 base64 字段）
  return stripLargeStrings(result);
}

/**
 * 递归剔除对象/数组中超过阈值的超长字符串字段，避免任何大 base64 进入 DOM。
 * 对小字符串、数字、布尔等原样保留。
 */
function stripLargeStrings(value: unknown): unknown {
  if (typeof value === 'string') {
    return value.length > LARGE_FIELD_THRESHOLD
      ? `${value.slice(0, LARGE_FIELD_THRESHOLD)}… [truncated for display]`
      : value;
  }
  if (Array.isArray(value)) {
    return value.map(stripLargeStrings);
  }
  if (typeof value === 'object' && value !== null) {
    const out: Record<string, unknown> = {};
    for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
      out[k] = stripLargeStrings(v);
    }
    return out;
  }
  return value;
}
