import { UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RecipesController } from './recipes.controller';

describe('Recipes controller security', () => {
  const makeUser = (overrides: Partial<{ userId: number; role: string }> = {}) => ({
    userId: 42,
    role: 'user',
    ...overrides,
  });

  const recipesServiceMock = {
    findAll: jest.fn(),
    findOne: jest.fn(),
    create: jest.fn(),
    update: jest.fn(),
    remove: jest.fn(),
    shareWithUser: jest.fn(),
    unshareWithUser: jest.fn(),
    setVisibility: jest.fn(),
  };

  const controller = new RecipesController(recipesServiceMock as any, {} as any);

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('JwtAuthGuard kräver autentisering på findAll', () => {
    const guard = new JwtAuthGuard(new Reflector());

    expect(() => guard.handleRequest(null, null, null)).toThrow(UnauthorizedException);
  });

  it('JwtAuthGuard tillåter autentiserad användare på findAll', () => {
    const guard = new JwtAuthGuard(new Reflector());
    const user = makeUser({ userId: 1 });

    expect(guard.handleRequest(null, user, null)).toBe(user);
  });

  it('findOne vidarebefordrar @CurrentUser.userId till service', () => {
    recipesServiceMock.findOne.mockResolvedValue({ id: 1 });

    controller.findOne(1, makeUser());

    expect(recipesServiceMock.findOne).toHaveBeenCalledWith(1, 42);
  });

  it('create vidarebefordrar @CurrentUser.userId till service', async () => {
    const dto = { name: 'test' };
    recipesServiceMock.create.mockResolvedValue({ id: 1 });

    await controller.create(dto as any, makeUser());

    expect(recipesServiceMock.create).toHaveBeenCalledWith(dto, 42);
  });

  it('update vidarebefordrar @CurrentUser.userId till service', async () => {
    const dto = { name: 'updated' };
    recipesServiceMock.update.mockResolvedValue({ id: 1 });

    await controller.update(1, dto as any, makeUser());

    expect(recipesServiceMock.update).toHaveBeenCalledWith(1, dto, 42);
  });

  it('remove vidarebefordrar @CurrentUser.userId till service', async () => {
    recipesServiceMock.remove.mockResolvedValue(undefined);

    await controller.remove(1, makeUser());

    expect(recipesServiceMock.remove).toHaveBeenCalledWith(1, 42);
  });

  it('shareRecipe vidarebefordrar userId och trimmar username', async () => {
    recipesServiceMock.shareWithUser.mockResolvedValue(undefined);

    await controller.shareRecipe(1, { username: '  alice  ' } as any, makeUser());

    expect(recipesServiceMock.shareWithUser).toHaveBeenCalledWith(1, 42, 'alice');
  });

  it('unshareRecipe vidarebefordrar userId och trimmar username', async () => {
    recipesServiceMock.unshareWithUser.mockResolvedValue(undefined);

    await controller.unshareRecipe(1, '  alice  ', makeUser());

    expect(recipesServiceMock.unshareWithUser).toHaveBeenCalledWith(1, 42, 'alice');
  });

  it('setVisibility vidarebefordrar userId till service', async () => {
    recipesServiceMock.setVisibility.mockResolvedValue(undefined);

    await controller.setVisibility(1, { isPublic: true } as any, makeUser());

    expect(recipesServiceMock.setVisibility).toHaveBeenCalledWith(1, 42, true);
  });
});
