/**
 * runtime_context 单元测试
 * Task 8.5: 验证 bizContext 与 currentAgent 的注入/读取行为
 */
import {
  injectToolRuntimeContext,
  readToolRuntimeContext,
  NetaToolRuntimeContext,
} from '../../../../src/modules/netaclaw/tools/runtime_context.js';

describe('injectToolRuntimeContext', () => {
  const baseArgs = { toolParam: 'value' };

  // 用例 1: runtime 为 undefined → 不注入 _netaRuntime，且清除外部伪造的 _netaRuntime
  it('runtime 为 undefined 时不注入运行时上下文', () => {
    const result = injectToolRuntimeContext(baseArgs, undefined);
    expect((result as any)._netaRuntime).toBeUndefined();
  });

  it('清除模型或用户传入的 _netaRuntime', () => {
    const result = injectToolRuntimeContext(
      { toolParam: 'value', _netaRuntime: { currentAgent: { id: 999 } } },
      { sessionCwd: '/foo' }
    );

    expect((result as any).toolParam).toBe('value');
    expect((result as any)._netaRuntime).toEqual({ sessionCwd: '/foo' });
  });

  // 用例 2: 只有 sessionCwd → _netaRuntime.sessionCwd 正确（已有行为）
  it('sessionCwd 注入正确', () => {
    const result = injectToolRuntimeContext(baseArgs, { sessionCwd: '/foo' });
    expect((result as any)._netaRuntime?.sessionCwd).toBe('/foo');
  });

  // 用例 3: bizContext 注入
  it('bizContext 注入正确', () => {
    const runtime: NetaToolRuntimeContext = {
      bizContext: { channelId: 5, roomName: 'R' },
    };
    const result = injectToolRuntimeContext(baseArgs, runtime);
    expect((result as any)._netaRuntime?.bizContext).toEqual({ channelId: 5, roomName: 'R' });
  });

  // 用例 4: currentAgent 注入
  it('currentAgent 注入正确', () => {
    const runtime: NetaToolRuntimeContext = {
      currentAgent: { id: 7, name: 'a', modelChannelId: 2, toolsets: ['crew'] },
    };
    const result = injectToolRuntimeContext(baseArgs, runtime);
    expect((result as any)._netaRuntime?.currentAgent).toEqual({
      id: 7,
      name: 'a',
      modelChannelId: 2,
      toolsets: ['crew'],
    });
  });

  it('userId 注入正确', () => {
    const result = injectToolRuntimeContext(baseArgs, { userId: 'user-1' });
    expect((result as any)._netaRuntime?.userId).toBe('user-1');
  });

  // 用例 5: bizContext 含函数 → 抛出 biz-context-not-serializable
  it('bizContext 含函数时抛出错误', () => {
    const runtime: any = {
      bizContext: { fn: () => 1 },
    };
    expect(() => injectToolRuntimeContext(baseArgs, runtime)).toThrow(
      'biz-context-not-serializable'
    );
  });

  // 用例 6: bizContext 含循环引用 → 抛出 biz-context-not-serializable
  it('bizContext 含循环引用时抛出错误', () => {
    const c: any = { a: 1 };
    c.self = c;
    expect(() => injectToolRuntimeContext(baseArgs, { bizContext: c })).toThrow(
      'biz-context-not-serializable'
    );
  });

  // 用例 7（补充）: currentAgent 含函数 → 也应抛出
  it('currentAgent 含函数时抛出错误', () => {
    const runtime: any = {
      currentAgent: { id: 1, name: 'a', modelChannelId: null, toolsets: [], extra: () => 0 },
    };
    expect(() => injectToolRuntimeContext(baseArgs, runtime)).toThrow(
      'biz-context-not-serializable'
    );
  });
});

describe('readToolRuntimeContext', () => {
  // 用例 7（原编号）: bizContext 读取
  it('bizContext 读取正确', () => {
    const result = readToolRuntimeContext({
      _netaRuntime: { bizContext: { channelId: 5 } },
    });
    expect(result?.bizContext?.channelId).toBe(5);
  });

  // 用例 8: currentAgent 读取
  it('currentAgent 读取正确', () => {
    const result = readToolRuntimeContext({
      _netaRuntime: {
        currentAgent: { id: 1, name: 'a', modelChannelId: null, toolsets: [] },
      },
    });
    expect(result?.currentAgent).toEqual({
      id: 1,
      name: 'a',
      modelChannelId: null,
      toolsets: [],
    });
  });

  // 用例 9: 无 _netaRuntime → 返回 undefined（已有行为）
  it('无 _netaRuntime 时返回 undefined', () => {
    expect(readToolRuntimeContext({})).toBeUndefined();
  });

  // 用例 10: 完整 round-trip（sessionCwd + userId + bizContext + currentAgent）
  it('inject + read 完整 round-trip', () => {
    const runtime: NetaToolRuntimeContext = {
      sessionCwd: '/workspace',
      userId: 'u-42',
      bizContext: { channelId: 42, roomName: 'main' },
      currentAgent: { id: 3, name: 'bot', modelChannelId: 1, toolsets: ['web', 'code'] },
    };
    const injected = injectToolRuntimeContext({ x: 1 }, runtime);
    const read = readToolRuntimeContext(injected as Record<string, unknown>);

    expect(read?.sessionCwd).toBe('/workspace');
    expect(read?.userId).toBe('u-42');
    expect(read?.bizContext).toEqual({ channelId: 42, roomName: 'main' });
    expect(read?.currentAgent).toEqual({
      id: 3,
      name: 'bot',
      modelChannelId: 1,
      toolsets: ['web', 'code'],
    });
  });
});
