import {
  INestApplication,
  UnauthorizedException,
  ValidationPipe,
  VersioningType,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Test } from '@nestjs/testing';
import * as request from 'supertest';
import validationOptions from '../utils/validation-options';
import { PenaltyController } from './penalty.controller';
import { PenaltyService } from './penalty.service';

describe('penalty catalog HTTP boundary', () => {
  let app: INestApplication;
  const penalty = {
    id: 8,
    description: 'Zu spät',
    amount: 5,
    createdAt: '2026-01-02T00:00:00.000Z',
  };
  const service = {
    getUserPenalties: jest.fn(() => [penalty]),
    getTeamPenalties: jest.fn(() => [penalty]),
    createPenalty: jest.fn(() => penalty),
    updatePenalty: jest.fn(() => penalty),
    deletePenalty: jest.fn(),
  };

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      controllers: [PenaltyController],
      providers: [{ provide: PenaltyService, useValue: service }],
    })
      .overrideGuard(AuthGuard('jwt'))
      .useValue({
        canActivate(context) {
          const httpRequest = context.switchToHttp().getRequest();
          if (httpRequest.headers.authorization !== 'Bearer user') {
            throw new UnauthorizedException();
          }
          httpRequest.user = { id: 42, role: { id: 2 } };
          return true;
        },
      })
      .compile();
    app = module.createNestApplication();
    app.setGlobalPrefix('api');
    app.enableVersioning({ type: VersioningType.URI });
    app.useGlobalPipes(new ValidationPipe(validationOptions));
    await app.init();
  });

  afterAll(() => app.close());
  beforeEach(() => jest.clearAllMocks());

  it('requires authentication and passes the actor to a team read', async () => {
    await request(app.getHttpServer()).get('/api/v1/penalty/5').expect(401);
    await request(app.getHttpServer())
      .get('/api/v1/penalty/5')
      .set('Authorization', 'Bearer user')
      .expect(200, [penalty]);
    expect(service.getTeamPenalties).toHaveBeenCalledWith(42, 5);
  });

  it('keeps the authenticated cross-team catalog route available', async () => {
    await request(app.getHttpServer()).get('/api/v1/penalty').expect(401);
    await request(app.getHttpServer())
      .get('/api/v1/penalty')
      .set('Authorization', 'Bearer user')
      .expect(200, [penalty]);
    expect(service.getUserPenalties).toHaveBeenCalledWith(42);
  });

  it('validates and strips create fields before invoking the service', async () => {
    await request(app.getHttpServer())
      .post('/api/v1/penalty')
      .set('Authorization', 'Bearer user')
      .send({
        teamId: 5,
        description: '  Zu spät  ',
        amount: 5.25,
        team: { id: 99 },
      })
      .expect(201, penalty);
    expect(service.createPenalty).toHaveBeenCalledWith(
      { teamId: 5, description: 'Zu spät', amount: 5.25 },
      42,
    );
  });

  it.each([
    [{ teamId: 5, description: '', amount: 5 }],
    [{ teamId: 5, description: 'x'.repeat(121), amount: 5 }],
    [{ teamId: 5, description: 'Test', amount: 0 }],
    [{ teamId: 5, description: 'Test', amount: 10000.01 }],
    [{ teamId: 5, description: 'Test', amount: 1.001 }],
    [{ teamId: true, description: 'Test', amount: 1 }],
  ])('rejects invalid create input: %p', async (body) => {
    await request(app.getHttpServer())
      .post('/api/v1/penalty')
      .set('Authorization', 'Bearer user')
      .send(body)
      .expect(422);
    expect(service.createPenalty).not.toHaveBeenCalled();
  });

  it('exposes update and delete with numeric IDs and safe payloads', async () => {
    await request(app.getHttpServer())
      .patch('/api/v1/penalty/8')
      .set('Authorization', 'Bearer user')
      .send({ description: ' Neu ', amount: 2 })
      .expect(200, penalty);
    expect(service.updatePenalty).toHaveBeenCalledWith(
      8,
      { description: 'Neu', amount: 2 },
      42,
    );

    await request(app.getHttpServer())
      .delete('/api/v1/penalty/8')
      .set('Authorization', 'Bearer user')
      .expect(204);
    expect(service.deletePenalty).toHaveBeenCalledWith(8, 42);
  });
});
