import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ROLES_KEY } from '../decorators/roles.decorator';
import { RolesGuard } from '../guards/roles.guard';

describe('RolesGuard', () => {
  let guard: RolesGuard;
  let mockReflector: { getAllAndOverride: ReturnType<typeof vi.fn> };
  let mockLogger: { warn: ReturnType<typeof vi.fn> };

  const createMockContext = (user?: { sub: string; role: string }) => {
    const mockRequest = {
      user,
      ip: '127.0.0.1',
      headers: {},
    };
    return {
      switchToHttp: () => ({
        getRequest: () => mockRequest,
      }),
      getHandler: () => ({ name: 'testHandler' }),
      getClass: () => ({ name: 'TestController' }),
    } as any;
  };

  beforeEach(() => {
    mockReflector = { getAllAndOverride: vi.fn() };
    mockLogger = { warn: vi.fn() };
    guard = new RolesGuard(mockReflector as any, mockLogger as any);
  });

  it('returns true when no roles are required', () => {
    mockReflector.getAllAndOverride.mockReturnValue(undefined);
    const context = createMockContext({ sub: 'user-1', role: 'BUYER' });
    expect(guard.canActivate(context)).toBe(true);
  });

  it('returns true when empty roles array is required', () => {
    mockReflector.getAllAndOverride.mockReturnValue([]);
    const context = createMockContext({ sub: 'user-1', role: 'BUYER' });
    expect(guard.canActivate(context)).toBe(true);
  });

  it('returns true when user has matching role', () => {
    mockReflector.getAllAndOverride.mockReturnValue(['ADMIN', 'MODERATOR']);
    const context = createMockContext({ sub: 'admin-1', role: 'ADMIN' });
    expect(guard.canActivate(context)).toBe(true);
  });

  it('returns false when user has non-matching role', () => {
    mockReflector.getAllAndOverride.mockReturnValue(['ADMIN']);
    const context = createMockContext({ sub: 'user-1', role: 'BUYER' });
    expect(guard.canActivate(context)).toBe(false);
  });

  it('returns false when no user on request', () => {
    mockReflector.getAllAndOverride.mockReturnValue(['ADMIN']);
    const context = createMockContext(undefined);
    expect(guard.canActivate(context)).toBe(false);
  });

  it('logs warning when access is denied', () => {
    mockReflector.getAllAndOverride.mockReturnValue(['ADMIN']);
    const context = createMockContext({ sub: 'user-1', role: 'BUYER' });
    guard.canActivate(context);
    expect(mockLogger.warn).toHaveBeenCalledWith(
      expect.stringContaining('Access denied'),
      'RolesGuard',
    );
  });

  it('reads ROLES_KEY metadata from handler and class', () => {
    mockReflector.getAllAndOverride.mockReturnValue(undefined);
    const context = createMockContext({ sub: 'user-1', role: 'BUYER' });
    guard.canActivate(context);

    expect(mockReflector.getAllAndOverride).toHaveBeenCalledWith(
      ROLES_KEY,
      [
        expect.objectContaining({ name: 'testHandler' }),
        expect.objectContaining({ name: 'TestController' }),
      ],
    );
  });
});
