import { ForbiddenException } from '@nestjs/common';
import type { ExecutionContext } from '@nestjs/common';
import type { Reflector } from '@nestjs/core';
import { PermissionsGuard } from './permissions.guard';
import type { PermissionsService } from './permissions.service';
import type { RequiredPermission } from './require-permission.decorator';

function contextWithUser(user?: { userId: string; roleId: string }) {
  return {
    getHandler: () => ({}),
    getClass: () => ({}),
    switchToHttp: () => ({ getRequest: () => ({ user }) }),
  } as unknown as ExecutionContext;
}

describe('PermissionsGuard (matrice relue en base — le JWT ne porte aucun droit)', () => {
  const reflector = (required?: RequiredPermission) =>
    ({ getAllAndOverride: () => required }) as unknown as Reflector;
  const service = (allowed: boolean) =>
    ({ can: jest.fn().mockResolvedValue(allowed) }) as unknown as PermissionsService;

  it('laisse passer une route sans @RequirePermission', async () => {
    const guard = new PermissionsGuard(reflector(undefined), service(false));
    await expect(guard.canActivate(contextWithUser())).resolves.toBe(true);
  });

  it('refuse si aucun utilisateur authentifié ne porte la requête', async () => {
    const guard = new PermissionsGuard(
      reflector({ category: 'WORK_ORDERS', right: 'view' }),
      service(true),
    );
    await expect(guard.canActivate(contextWithUser(undefined))).rejects.toThrow(
      ForbiddenException,
    );
  });

  it('autorise quand la matrice accorde le droit', async () => {
    const permissions = service(true);
    const guard = new PermissionsGuard(
      reflector({ category: 'WORK_ORDERS', right: 'create' }),
      permissions,
    );
    await expect(
      guard.canActivate(contextWithUser({ userId: 'u1', roleId: 'r1' })),
    ).resolves.toBe(true);
    expect(permissions.can).toHaveBeenCalledWith('r1', 'WORK_ORDERS', 'create');
  });

  it('refuse (403, droit nommé) quand la matrice ne l’accorde pas', async () => {
    const guard = new PermissionsGuard(
      reflector({ category: 'SETTINGS', right: 'delete' }),
      service(false),
    );
    await expect(
      guard.canActivate(contextWithUser({ userId: 'u1', roleId: 'r1' })),
    ).rejects.toThrow(/SETTINGS\.delete/);
  });
});
