import { ManagementService } from './management.service';

describe('ManagementService', () => {
  const makeService = () => {
    const prisma = {
      appData: { findMany: jest.fn() },
      projectMember: { findMany: jest.fn() },
      version: { findMany: jest.fn() },
      versionPlan: { findMany: jest.fn() },
      devTask: { findMany: jest.fn() },
      testCase: { findMany: jest.fn() },
      bug: { findMany: jest.fn() },
      xiaobaoRiskSummary: { findMany: jest.fn() },
    };
    const rbac = {
      assertGlobalPermission: jest.fn().mockResolvedValue({ actorId: 'm-manager', via: 'permission' }),
    };
    return { prisma, rbac, service: new ManagementService(prisma as any, rbac as any) };
  };

  it('requires management view permission through the RBAC adapter', async () => {
    const { rbac, service } = makeService();
    rbac.assertGlobalPermission.mockRejectedValue(new Error('forbidden'));

    await expect(service.getOverview({ actorId: 'm-dev', permissions: [] })).rejects.toThrow('forbidden');
  });

  it('returns an empty dashboard when actor has no managed projects', async () => {
    const { prisma, rbac, service } = makeService();
    prisma.projectMember.findMany.mockResolvedValue([]);

    await expect(service.getOverview({ actorId: 'm-dev', permissions: ['management:view'] })).resolves.toEqual({
      activeVersionCount: 0,
      overdueItemCount: 0,
      blockedItemCount: 0,
      riskCounts: {},
      memberLoads: [],
      activeVersions: [],
      overdueItems: [],
      blockedItems: [],
      highRiskVersions: [],
    });
    expect(rbac.assertGlobalPermission).toHaveBeenCalledWith({
      actorId: 'm-dev',
      permissions: ['management:view'],
      requiredPermissions: ['management:view'],
    });
    expect(prisma.appData.findMany).not.toHaveBeenCalled();
  });

  it('aggregates active versions, overdue work, blockers, risks, and member load from relation tables only', async () => {
    const { prisma, service } = makeService();
    prisma.projectMember.findMany.mockResolvedValue([{ projectId: 'project-1' }]);
    prisma.version.findMany.mockResolvedValue([
      { id: 'ver-1', projectId: 'project-1', name: 'V1', releaseDate: new Date('2026-07-20T00:00:00.000Z') },
    ]);
    prisma.versionPlan.findMany.mockResolvedValue([
      { id: 'plan-1', versionId: 'ver-1', title: '产品方案', ownerId: 'm-pm', status: 'in_progress', expectedEndAt: new Date('2026-07-01T00:00:00.000Z') },
    ]);
    prisma.devTask.findMany.mockResolvedValue([
      { id: 'task-1', versionId: 'ver-1', title: '接口开发', assigneeId: 'm-dev', status: 'in_progress', isBlocked: true, expectedEndAt: new Date('2026-07-01T00:00:00.000Z') },
    ]);
    prisma.testCase.findMany.mockResolvedValue([
      { id: 'tc-1', versionId: 'ver-1', title: '权限测试', assigneeId: 'm-qa', status: 'running', plannedEndAt: new Date('2026-07-01T00:00:00.000Z') },
    ]);
    prisma.bug.findMany.mockResolvedValue([
      { id: 'bug-1', versionId: 'ver-1', title: '线上缺陷', assigneeId: 'm-dev', status: 'open', plannedFixAt: new Date('2026-07-01T00:00:00.000Z') },
    ]);
    prisma.xiaobaoRiskSummary.findMany.mockResolvedValue([
      { versionId: 'ver-1', riskLevel: 'likely_delayed', riskScore: 82 },
    ]);

    const result = await service.getOverview({
      actorId: 'm-manager',
      permissions: [],
      now: new Date('2026-07-08T00:00:00.000Z'),
    });

    expect(result.activeVersionCount).toBe(1);
    expect(result.overdueItemCount).toBe(4);
    expect(result.blockedItemCount).toBe(1);
    expect(result.riskCounts).toEqual({ likely_delayed: 1 });
    expect(result.memberLoads).toEqual([
      { memberId: 'm-dev', openItemCount: 2 },
      { memberId: 'm-pm', openItemCount: 1 },
      { memberId: 'm-qa', openItemCount: 1 },
    ]);
    expect(prisma.appData.findMany).not.toHaveBeenCalled();
  });
});
