/**
 * InternalAppEventsService L1 集成测试（P1.5）
 *
 * 覆盖 PR #368 第 5 轮 AI Review 提出的"P1.5 零集成测试"风险：
 * - employeeSlug filter 隔离：相当于 /me/events IDOR 回归（list 在传 employeeSlug
 *   时只返该 slug 行，admin controller 不传 / me controller 强制传——controller 层很薄）
 * - admin filter happy path：actorRole / eventType / from-to / outcome 单独+组合过滤
 * - emit best-effort：FK 违反时不抛、调用方不回滚（best-effort 不变量）
 */

import { INestApplication } from '@nestjs/common';
import { createTestApp } from '../../helpers/app.helper';
import {
  InternalAppEventsService,
  EventType,
} from '@/modules/internal-app-platform/services/events.service';
import { PrismaService } from '@/core/database/prisma/prisma.service';

const T = 't_iap_events_';
const ORG_CODE = `${T}org`;

describe('InternalAppEventsService (L1)', () => {
  let app: INestApplication;
  let eventsSvc: InternalAppEventsService;
  let prisma: PrismaService;
  let organizationId: string;
  let userId: string;

  beforeAll(async () => {
    app = await createTestApp();
    eventsSvc = app.get(InternalAppEventsService);
    prisma = app.get(PrismaService);

    const org = await prisma.organization.upsert({
      where: { code: ORG_CODE },
      create: {
        code: ORG_CODE,
        name: `${T}org`,
        status: 'ACTIVE',
        isActive: true,
        settings: {},
        financialConfig: {},
        complianceConfig: {},
        order: 0,
        metadata: {},
      } as any,
      update: {},
    });
    organizationId = org.id;

    const u = await prisma.user.create({
      data: {
        username: `${T}admin`,
        email: `${T}admin@test.local`,
        passwordHash: 'PLACEHOLDER',
        displayName: `${T}admin`,
        status: 'ACTIVE',
        source: 'LOCAL',
        tenantId: 'default',
      },
    });
    userId = u.id;
  });

  afterAll(async () => {
    await prisma.internalAppEvent.deleteMany({
      where: { employeeSlug: { startsWith: T } },
    });
    await prisma.user.deleteMany({ where: { email: { startsWith: T } } });
    await prisma.organization.deleteMany({ where: { code: ORG_CODE } });
    await app.close();
  });

  beforeEach(async () => {
    await prisma.internalAppEvent.deleteMany({
      where: { employeeSlug: { startsWith: T } },
    });
  });

  it('list employeeSlug filter：只返该 slug 行（/me/events IDOR 回归）', async () => {
    await eventsSvc.emit({
      eventType: EventType.TOKEN_ISSUED,
      actorRole: 'OWNER',
      organizationId,
      employeeSlug: `${T}alice`,
      payload: {},
    });
    await eventsSvc.emit({
      eventType: EventType.TOKEN_ISSUED,
      actorRole: 'OWNER',
      organizationId,
      employeeSlug: `${T}bob`,
      payload: {},
    });

    const aliceOnly = await eventsSvc.list({
      organizationId,
      employeeSlug: `${T}alice`,
    });
    expect(aliceOnly.total).toBe(1);
    expect(aliceOnly.items[0].employeeSlug).toBe(`${T}alice`);

    const all = await eventsSvc.list({ organizationId });
    expect(all.total).toBe(2);
  });

  it('list admin 过滤组合：actorRole + eventType + from/to + outcome', async () => {
    const t0 = new Date(Date.now() - 3600 * 1000);
    const t1 = new Date(Date.now() - 1800 * 1000);
    const t2 = new Date();

    // 准备数据：3 条不同 actorRole/eventType/outcome
    await prisma.internalAppEvent.createMany({
      data: [
        {
          actorRole: 'OWNER',
          eventType: EventType.TOKEN_ISSUED,
          outcome: 'OK',
          organizationId,
          employeeSlug: `${T}u1`,
          payload: {},
          createdAt: t0,
        },
        {
          actorRole: 'ADMIN',
          eventType: EventType.APP_DISABLED_BY_ADMIN,
          outcome: 'OK',
          organizationId,
          employeeSlug: `${T}u1`,
          payload: {},
          createdAt: t1,
        },
        {
          actorRole: 'OWNER',
          eventType: EventType.APP_DEPLOY_FAILED,
          outcome: 'FAIL',
          errorCode: 'BUILD_ERR',
          organizationId,
          employeeSlug: `${T}u1`,
          payload: {},
          createdAt: t2,
        },
      ],
    });

    // actorRole=ADMIN
    const admins = await eventsSvc.list({ organizationId, actorRole: 'ADMIN' });
    expect(admins.total).toBe(1);
    expect(admins.items[0].eventType).toBe(EventType.APP_DISABLED_BY_ADMIN);

    // eventType IN
    const tokenOrDeploy = await eventsSvc.list({
      organizationId,
      eventTypes: [EventType.TOKEN_ISSUED, EventType.APP_DEPLOY_FAILED],
    });
    expect(tokenOrDeploy.total).toBe(2);

    // outcome=FAIL
    const fails = await eventsSvc.list({ organizationId, outcome: 'FAIL' });
    expect(fails.total).toBe(1);
    expect(fails.items[0].errorCode).toBe('BUILD_ERR');

    // from/to 窗口：只取 t1 之后
    const recent = await eventsSvc.list({
      organizationId,
      from: new Date(t1.getTime() - 1000),
    });
    expect(recent.total).toBe(2);
  });

  it('emit best-effort：FK 违反时不抛、不影响调用方', async () => {
    // actorId 不存在 → actor User FK 违反，emit 应吞掉异常（warn 不抛）
    await expect(
      eventsSvc.emit({
        eventType: EventType.TOKEN_ISSUED,
        actorRole: 'OWNER',
        organizationId,
        employeeSlug: `${T}ghost`,
        actorId: '00000000-0000-0000-0000-000000000000',
        payload: {},
      }),
    ).resolves.toBeUndefined();

    // 验证确实没写入
    const after = await prisma.internalAppEvent.count({
      where: { employeeSlug: `${T}ghost` },
    });
    expect(after).toBe(0);
  });

  it('emit 写入 actorId：admin 强制操作的 actor 链路闭环', async () => {
    await eventsSvc.emit({
      eventType: EventType.APP_DISABLED_BY_ADMIN,
      actorRole: 'ADMIN',
      organizationId,
      employeeSlug: `${T}victim`,
      actorId: userId,
      payload: { reason: 'test' },
    });

    const r = await eventsSvc.list({
      organizationId,
      employeeSlug: `${T}victim`,
    });
    expect(r.total).toBe(1);
    expect(r.items[0].actorId).toBe(userId);
    expect(r.items[0].actorEmail).toBe(`${T}admin@test.local`);
  });
});
