/**
 * 工具运行时上下文：在工具调用参数中注入/读取运行时信息
 */

/** 业务上下文：JSON-safe 的键值对 */
export interface NetaToolRuntimeBizContext {
  channelId?: number;
  roomName?: string;
  // 仅限 JSON-safe 类型（原始值 / 数组 / 普通对象）
  [k: string]: string | number | boolean | null | undefined | object | any[];
}

/** 当前 Agent 信息 */
export interface NetaToolRuntimeCurrentAgent {
  id: number;
  name: string;
  modelChannelId: number | null;
  toolsets: string[];
}

/** 工具运行时上下文接口 */
export interface NetaToolRuntimeContext {
  sessionCwd?: string | null;
  workspaceRoots?: string[];
  userId?: string | number | null;
  bizContext?: NetaToolRuntimeBizContext;
  currentAgent?: NetaToolRuntimeCurrentAgent;
}

/**
 * 递归验证值是否 JSON-safe（不含函数、Symbol、BigInt、循环引用、非普通对象实例）
 * 不安全时抛出 Error('biz-context-not-serializable')
 * visited 用于检测循环引用
 */
function ensureJsonSafe(v: unknown, visited: Set<object> = new Set()): void {
  if (v === null) return;
  const t = typeof v;
  if (t === 'string' || t === 'number' || t === 'boolean' || t === 'undefined') return;
  if (Array.isArray(v)) {
    if (visited.has(v)) throw new Error('biz-context-not-serializable');
    visited.add(v);
    for (const item of v) ensureJsonSafe(item, visited);
    return;
  }
  if (t === 'object') {
    // 循环引用检测
    if (visited.has(v as object)) throw new Error('biz-context-not-serializable');
    visited.add(v as object);
    const proto = Object.getPrototypeOf(v as object);
    // 只允许普通对象（原型为 Object.prototype 或 null）
    if (proto !== Object.prototype && proto !== null) {
      throw new Error('biz-context-not-serializable');
    }
    // 递归检查各字段
    for (const key of Object.keys(v as object)) {
      ensureJsonSafe((v as Record<string, unknown>)[key], visited);
    }
    return;
  }
  // function / symbol / bigint 等均不安全
  throw new Error('biz-context-not-serializable');
}

/**
 * 将运行时上下文注入工具调用参数
 * - runtime 为 undefined → 原样返回 args（无副作用）
 * - 所有字段均为空 → 原样返回 args
 * - 否则返回 {...args, _netaRuntime: {...}} 仅包含有值的字段
 */
export function injectToolRuntimeContext<T extends Record<string, unknown>>(
  args: T,
  runtime: NetaToolRuntimeContext | undefined
): T & { _netaRuntime?: NetaToolRuntimeContext } {
  const { _netaRuntime, ...safeArgs } = args;

  if (!runtime) {
    return safeArgs as T & { _netaRuntime?: NetaToolRuntimeContext };
  }

  // 解析 sessionCwd
  const sessionCwd =
    typeof runtime.sessionCwd === 'string' && runtime.sessionCwd.trim()
      ? runtime.sessionCwd.trim()
      : undefined;

  // 解析 workspaceRoots
  const workspaceRoots = Array.isArray(runtime.workspaceRoots)
    ? runtime.workspaceRoots.filter(
        (item): item is string => typeof item === 'string' && item.trim().length > 0
      )
    : [];

  const userId =
    typeof runtime.userId === 'string' || typeof runtime.userId === 'number'
      ? runtime.userId
      : undefined;

  // 解析 bizContext（JSON-safe 校验）
  let bizContext: NetaToolRuntimeBizContext | undefined;
  if (runtime.bizContext !== undefined) {
    ensureJsonSafe(runtime.bizContext);
    bizContext = runtime.bizContext;
  }

  // 解析 currentAgent（JSON-safe 校验）
  let currentAgent: NetaToolRuntimeCurrentAgent | undefined;
  if (runtime.currentAgent !== undefined) {
    ensureJsonSafe(runtime.currentAgent);
    currentAgent = runtime.currentAgent;
  }

  // 若所有字段均为空，原样返回
  if (!sessionCwd && workspaceRoots.length === 0 && userId === undefined && bizContext === undefined && currentAgent === undefined) {
    return safeArgs as T & { _netaRuntime?: NetaToolRuntimeContext };
  }

  return {
    ...safeArgs,
    _netaRuntime: {
      ...(sessionCwd ? { sessionCwd } : {}),
      ...(workspaceRoots.length > 0 ? { workspaceRoots } : {}),
      ...(userId !== undefined ? { userId } : {}),
      ...(bizContext !== undefined ? { bizContext } : {}),
      ...(currentAgent !== undefined ? { currentAgent } : {}),
    },
  } as T & { _netaRuntime?: NetaToolRuntimeContext };
}

/**
 * 从工具调用参数中读取运行时上下文
 * 返回 undefined 表示未注入
 */
export function readToolRuntimeContext(
  params: Record<string, unknown> | undefined
): NetaToolRuntimeContext | undefined {
  const runtime = params?._netaRuntime;
  if (!runtime || typeof runtime !== 'object' || Array.isArray(runtime)) {
    return undefined;
  }

  const value = runtime as Record<string, unknown>;

  // 读取 bizContext
  const bizContext =
    value.bizContext !== undefined &&
    value.bizContext !== null &&
    typeof value.bizContext === 'object' &&
    !Array.isArray(value.bizContext)
      ? (value.bizContext as NetaToolRuntimeBizContext)
      : undefined;

  // 读取 currentAgent
  const currentAgent =
    value.currentAgent !== undefined &&
    value.currentAgent !== null &&
    typeof value.currentAgent === 'object' &&
    !Array.isArray(value.currentAgent)
      ? (value.currentAgent as NetaToolRuntimeCurrentAgent)
      : undefined;

  return {
    sessionCwd: typeof value.sessionCwd === 'string' ? value.sessionCwd : null,
    workspaceRoots: Array.isArray(value.workspaceRoots)
      ? value.workspaceRoots.filter((item): item is string => typeof item === 'string')
      : [],
    ...(typeof value.userId === 'string' || typeof value.userId === 'number' ? { userId: value.userId } : {}),
    ...(bizContext !== undefined ? { bizContext } : {}),
    ...(currentAgent !== undefined ? { currentAgent } : {}),
  };
}
