/**
 * FFAI Agent — tools API 集成测试
 *
 * 覆盖：
 *   - GET /agent/tools 列出当前 surface 可见的 8 个内置 tool
 *   - PR4.5 mode 过滤：Plan REQUIRED 隐藏 writeAction tool（approval_submit）+ 调用 403
 *   - PR4a surface 大小写归一化：'WEB' / 'web' 都应被识别
 */
import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { PrismaService } from '@/core/database/prisma/prisma.service';
import { cleanupDatabase } from '../../helpers/cleanup.helper';
import { createTestApp } from '../../helpers/app.helper';
import { setupIntegrationTest } from '../../helpers/test-setup.helper';

describe('FFAI Agent - Tools API', () => {
  let app: INestApplication;
  let prisma: PrismaService;
  let adminToken: string;
  let orgId: string;

  beforeAll(async () => {
    app = await createTestApp();
    prisma = app.get<PrismaService>(PrismaService);
  });

  beforeEach(async () => {
    const ctx = await setupIntegrationTest(app, prisma);
    adminToken = ctx.adminToken;
    const org = await prisma.organization.findFirst({ orderBy: { createdAt: 'asc' } });
    orgId = org!.id;
  });

  afterEach(async () => {
    await cleanupDatabase(prisma);
  });

  afterAll(async () => {
    await app.close();
  });

  it('[AGENT-TOOL-001] GET /agent/tools 默认 surface=web 列出 6 个 tool（3 业务 + 3 控制；delegate_task + file_save 实际共 8 个）', async () => {
    const res = await request(app.getHttpServer())
      .get('/api/v1/agent/tools?surface=web')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .expect(200);

    const names = res.body.data.items.map((t: { name: string }) => t.name);
    expect(names).toEqual(
      expect.arrayContaining([
        'project_query',
        'knowledge_query',
        'approval_submit',
        'EnterPlanMode',
        'ExitPlanMode',
        'SetPermissionMode',
        'delegate_task',
        'file_save',
      ]),
    );
  });

  it('[AGENT-TOOL-002] PR4a surface 大小写归一化：?surface=WEB 与 ?surface=web 返回同一 8 个工具', async () => {
    const lower = await request(app.getHttpServer())
      .get('/api/v1/agent/tools?surface=web')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId);
    const upper = await request(app.getHttpServer())
      .get('/api/v1/agent/tools?surface=WEB')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId);

    const lowerNames = lower.body.data.items.map((t: { name: string }) => t.name).sort();
    const upperNames = upper.body.data.items.map((t: { name: string }) => t.name).sort();
    expect(upperNames).toEqual(lowerNames);
    expect(lowerNames.length).toBeGreaterThanOrEqual(6);
  });

  it('[AGENT-TOOL-003] PR4.5 Plan mode REQUIRED 隐藏 writeAction tool（approval_submit / file_save）', async () => {
    const sess = await prisma.agentSession.create({
      data: {
        organizationId: orgId,
        createdById: '00000000-0000-0000-0000-000000000000',
        planMode: 'REQUIRED',
      },
    });
    const res = await request(app.getHttpServer())
      .get(`/api/v1/agent/tools?surface=web&sessionId=${sess.id}`)
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .expect(200);

    const names = res.body.data.items.map((t: { name: string }) => t.name);
    expect(names).not.toContain('approval_submit');
    expect(names).not.toContain('file_save');
    // 控制工具仍可见（否则用户无法切回去）
    expect(names).toContain('EnterPlanMode');
    expect(names).toContain('ExitPlanMode');
  });

  it('[AGENT-TOOL-005] PR11.3 surface=web 默认能力集（仅 shell.openExternal）不应可见任一 client:* 工具', async () => {
    const res = await request(app.getHttpServer())
      .get('/api/v1/agent/tools?surface=web')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .expect(200);

    const names: string[] = res.body.data.items.map((t: { name: string }) => t.name);
    // 这 7 个 client:* 工具的 requiredCapabilities 全不在 web 默认能力集里 → 都该被过滤
    for (const hidden of [
      'File.read',
      'File.write',
      'File.list',
      'Clipboard.read',
      'Clipboard.write',
      'Notify.push',
      'Shell.exec',
    ]) {
      expect(names).not.toContain(hidden);
    }
    // 正向兜底：Shell.openExternal 在 web 默认能力集里，必须可见——
    // 任一处漂移（descriptor surface / SURFACE_DEFAULT_CAPABILITIES.web）反向用例都抓不到
    expect(names).toContain('Shell.openExternal');
  });

  it('[AGENT-TOOL-006] PR11.3 surface=desktop 默认能力集全开，client:* 工具应全部可见', async () => {
    const res = await request(app.getHttpServer())
      .get('/api/v1/agent/tools?surface=desktop')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .expect(200);

    const names: string[] = res.body.data.items.map((t: { name: string }) => t.name);
    expect(names).toEqual(
      expect.arrayContaining([
        'File.read',
        'File.write',
        'File.list',
        'Clipboard.read',
        'Clipboard.write',
        'Notify.push',
        'Shell.exec',
      ]),
    );
  });

  it('[AGENT-TOOL-007] PR11.3 surface 过滤优先于 capability 过滤——surface=mobile（不在 LocalFile.* 允许集）不可见', async () => {
    // LocalFile.* 的 availability.surface = ['desktop', 'cli']；mobile 不在内
    const res = await request(app.getHttpServer())
      .get('/api/v1/agent/tools?surface=mobile')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .expect(200);

    const names: string[] = res.body.data.items.map((t: { name: string }) => t.name);
    expect(names).not.toContain('File.read');
    expect(names).not.toContain('Shell.exec');
  });

  it('[AGENT-TOOL-004] PR4.5 Plan mode 下硬调 writeAction tool → 403', async () => {
    const sess = await prisma.agentSession.create({
      data: {
        organizationId: orgId,
        createdById: '00000000-0000-0000-0000-000000000000',
        planMode: 'REQUIRED',
      },
    });
    await request(app.getHttpServer())
      .post('/api/v1/agent/tools/approval_submit')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .send({
        sessionId: sess.id,
        input: { processDefinitionKey: 'X', businessType: 'X', title: 't' },
        surface: 'web',
      })
      .expect(403);
  });

  // ─────────── Phase 2 新工具 smoke ───────────

  it('[AGENT-TOOL-PHASE2-SMOKE] Phase 2 新增的 10 个工具全部在 ToolRegistry 中注册（GET /agent/tools 可见）', async () => {
    const res = await request(app.getHttpServer())
      .get('/api/v1/agent/tools?surface=web')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .expect(200);
    const names = (res.body.data.items as Array<{ name: string }>).map((t) => t.name);
    const phase2 = [
      'web_search',
      'web_fetch',
      'TodoWrite',
      'TaskCreate',
      'TaskUpdate',
      'TaskList',
      'TaskStop',
      'SendMessage',
      'CronCreate',
      'CronList',
      'CronUpdate',
      'CronDelete',
    ];
    for (const t of phase2) {
      expect(names).toContain(t);
    }
  });

  it('[AGENT-TOOL-PHASE2-CRONLIST] CronList 工具调用通路通（route 注册 + service 路由）—— 空 list 返 200', async () => {
    // CronList 是 read tool，无副作用；走完整 controller → ToolRegistry → service → prisma 链路
    const sess = await prisma.agentSession.create({
      data: {
        organizationId: orgId,
        createdById: '00000000-0000-0000-0000-000000000000',
        title: 'cron-list-smoke',
      },
    });
    const res = await request(app.getHttpServer())
      .post('/api/v1/agent/tools/CronList')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .send({ input: {}, sessionId: sess.id, surface: 'web' })
      .expect(201);
    expect(res.body.data.ok).toBe(true);
  });
});
