import { ForbiddenException, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthContextService } from './auth-context.service';
import { PermissionGuard } from './permission.guard';
import { RequirePermission } from './permission.decorator';

describe('PermissionGuard', () => {
  it('rejects protected routes when no current user can be resolved', async () => {
    const guard = new PermissionGuard(
      new Reflector(),
      { resolveCurrentUser: jest.fn().mockResolvedValue(null) } as unknown as AuthContextService,
      { can: jest.fn() } as any,
    );
    const handler = decorate(() => undefined, 'product:create');

    await expect(guard.canActivate(contextFor(handler))).rejects.toBeInstanceOf(UnauthorizedException);
  });

  it('rejects users without the required permission', async () => {
    const guard = new PermissionGuard(
      new Reflector(),
      { resolveCurrentUser: jest.fn().mockResolvedValue({ id: 'user-1', roleId: 'role-dev' }) } as any,
      { can: jest.fn().mockResolvedValue(false) } as any,
    );
    const handler = decorate(() => undefined, 'product:delete');

    await expect(guard.canActivate(contextFor(handler))).rejects.toBeInstanceOf(ForbiddenException);
  });

  it('passes params-derived resource scope to the permission service', async () => {
    const can = jest.fn().mockResolvedValue(true);
    const guard = new PermissionGuard(
      new Reflector(),
      { resolveCurrentUser: jest.fn().mockResolvedValue({ id: 'user-1', roleId: 'role-dev' }) } as any,
      { can } as any,
    );
    const handler = decorate(() => undefined, 'version.devtask:manage', {
      versionIdParam: 'versionId',
      projectIdParam: 'projectId',
      productIdParam: 'productId',
    });

    await expect(guard.canActivate(contextFor(handler, {
      productId: 'product-1',
      projectId: 'project-1',
      versionId: 'version-1',
    }))).resolves.toBe(true);

    expect(can).toHaveBeenCalledWith(
      { id: 'user-1', roleId: 'role-dev' },
      'version.devtask:manage',
      { productId: 'product-1', projectId: 'project-1', versionId: 'version-1' },
    );
  });
});

function decorate(handler: Function, permission: string, scope?: {
  productIdParam?: string;
  projectIdParam?: string;
  versionIdParam?: string;
}) {
  RequirePermission(permission, scope)(handler as any, undefined as any, undefined as any);
  return handler;
}

function contextFor(handler: Function, params: Record<string, string> = {}) {
  return {
    getHandler: () => handler,
    getClass: () => class TestController {},
    switchToHttp: () => ({
      getRequest: () => ({ params, headers: {} }),
    }),
  } as any;
}
