/**
 * FFAI Agent — messages API 集成测试（含 cancel endpoint）
 *
 * 覆盖：
 *   - POST /agent/messages 跨 org sessionId 必拒（INV-1，关闭 trajectory 投毒向量）
 *   - POST /agent/messages 不存在 session 返 404
 *   - POST /agent/messages/:turnId/cancel 不存在 turn 返 {cancelled:false}
 *   - POST /agent/messages/:turnId/cancel 无 JWT 返 401
 *
 * 不覆盖：完整 runTurn happy path（需 mock provider，工作量大；进 follow-up PR）
 */
import { INestApplication } from '@nestjs/common';
import request from 'supertest';
import { PrismaService } from '@/core/database/prisma/prisma.service';
import { AgentMessagesService } from '@/modules/agent/services/messages.service';
import { cleanupDatabase } from '../../helpers/cleanup.helper';
import { createTestApp } from '../../helpers/app.helper';
import { setupIntegrationTest } from '../../helpers/test-setup.helper';

describe('FFAI Agent - Messages 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-MSG-001] POST /agent/messages 跨 org sessionId 必拒（INV-1）', async () => {
    const otherOrg = await prisma.organization.create({
      data: { code: `T_msg_${Date.now()}`, name: 'other-org-msg' },
    });
    const sess = await prisma.agentSession.create({
      data: {
        organizationId: otherOrg.id,
        createdById: '00000000-0000-0000-0000-000000000000',
        title: 'cross-org-msg',
      },
    });

    await request(app.getHttpServer())
      .post('/api/v1/agent/messages')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .send({ sessionId: sess.id, prompt: 'hi' })
      .expect(403);
  });

  it('[AGENT-MSG-002] POST /agent/messages 不存在 session 返 404', async () => {
    await request(app.getHttpServer())
      .post('/api/v1/agent/messages')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .send({
        sessionId: '00000000-0000-0000-0000-000000000000',
        prompt: 'hi',
      })
      .expect(404);
  });

  it('[AGENT-MSG-003] POST /agent/messages sessionId 或 prompt 缺失返 400', async () => {
    await request(app.getHttpServer())
      .post('/api/v1/agent/messages')
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .send({ sessionId: 'x' })
      .expect(400);
  });

  it('[AGENT-MSG-004] POST /agent/messages/:turnId/cancel 不存在 turn 返 {cancelled:false}', async () => {
    const res = await request(app.getHttpServer())
      .post(`/api/v1/agent/messages/${'00000000-0000-0000-0000-000000000000'}/cancel`)
      .set('Authorization', `Bearer ${adminToken}`)
      .set('X-Organization-Id', orgId)
      .expect(200);
    expect(res.body.data.cancelled).toBe(false);
  });

  it('[AGENT-MSG-005] POST /agent/messages/:turnId/cancel 无 JWT 返 401', async () => {
    await request(app.getHttpServer())
      .post(`/api/v1/agent/messages/${'00000000-0000-0000-0000-000000000000'}/cancel`)
      .set('X-Organization-Id', orgId)
      .expect(401);
  });

  it('[AGENT-MSG-006] POST /agent/messages/:turnId/cancel 跨 org 必拒 403（cancelTurn ACL 回归）', async () => {
    /**
     * 直接通过 service 注册一个 org A 的 activeTurn，然后用 org B 的 actor 调 cancel endpoint，
     * 期望 403。Endpoint 内调 cancelTurn(turnId, orgB) → entry.orgId !== orgB → ForbiddenException。
     */
    const otherOrg = await prisma.organization.create({
      data: { code: `T_msg_cancel_${Date.now()}`, name: 'other-org-cancel' },
    });
    // 直接在 service 注册一个 org B 的 activeTurn（避免触发真 LLM）；activeTurns 为 private，
    // 测试用 cast 访问内部 map 是已知的"测试侧 white-box"模式。
    const messagesService = app.get(AgentMessagesService) as unknown as {
      activeTurns: Map<string, { controller: AbortController; orgId: string }>;
    };
    const turnId = '00000000-0000-0000-0000-0000000000aa';
    messagesService.activeTurns.set(turnId, { controller: new AbortController(), orgId: otherOrg.id });

    try {
      // 当前 admin 在 orgId（非 otherOrg），cancel 应被 ACL 拒
      await request(app.getHttpServer())
        .post(`/api/v1/agent/messages/${turnId}/cancel`)
        .set('Authorization', `Bearer ${adminToken}`)
        .set('X-Organization-Id', orgId)
        .expect(403);
    } finally {
      messagesService.activeTurns.delete(turnId);
    }
  });
});
