import { INestApplication, ValidationPipe } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import request from 'supertest';
import { App } from 'supertest/types';
import { AppModule } from '../src/app.module';
import { AccountResponse } from '../src/auth/dto/account.response';
import { PrismaService } from '../src/prisma/prisma.service';

/** Corpo de erro padrão do Nest, tipado para o lint não ver `any`. */
type ErroHttp = { message: string | string[]; statusCode: number };

describe('POST /auth/register (e2e)', () => {
  let app: INestApplication<App>;
  let prisma: PrismaService;

  beforeAll(async () => {
    const mod = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();
    app = mod.createNestApplication();
    // Mesma configuração do main.ts, senão o teste não exercita a validação real.
    app.useGlobalPipes(
      new ValidationPipe({
        whitelist: true,
        forbidNonWhitelisted: true,
        transform: true,
      }),
    );
    await app.init();
    // Servidor escutando **uma vez** por suíte: sem isto o supertest faz
    // listen/close por requisição, e o ciclo repetido produz "socket hang up"
    // aleatório na suíte completa — gate que falha sozinho não serve de gate.
    await app.listen(0);
    prisma = app.get(PrismaService);
  });

  afterAll(async () => {
    await prisma.user.deleteMany({
      where: { email: { contains: 'participante' } },
    });
    await app.close();
  });

  const conta = (sufixo: string) => ({
    email: `participante${sufixo}@e2e.teste`,
    password: 'senhaSegura123',
    displayName: 'Participante',
  });

  it('cria a conta e não devolve senha nem hash', async () => {
    const res = await request(app.getHttpServer())
      .post('/auth/register')
      .send(conta('-ok'))
      .expect(201);

    expect(res.body).toMatchObject({
      email: 'participante-ok@e2e.teste',
      displayName: 'Participante',
    });
    expect((res.body as AccountResponse).id).toBeDefined();
    expect(JSON.stringify(res.body)).not.toContain('senhaSegura123');
    expect(res.body).not.toHaveProperty('password');
    expect(res.body).not.toHaveProperty('passwordHash');
  });

  it('recusa e-mail duplicado apontando para o login', async () => {
    await request(app.getHttpServer())
      .post('/auth/register')
      .send(conta('-dup'))
      .expect(201);

    const res = await request(app.getHttpServer())
      .post('/auth/register')
      .send(conta('-dup'))
      .expect(409);

    expect((res.body as ErroHttp).message).toMatch(/login/i);
  });

  it('recusa e-mail malformado', async () => {
    const res = await request(app.getHttpServer())
      .post('/auth/register')
      .send({ ...conta('-x'), email: 'nao-e-email' })
      .expect(400);

    expect(JSON.stringify(res.body)).toMatch(/e-mail inválido/i);
  });

  it('recusa senha curta sem ecoar o valor enviado', async () => {
    const res = await request(app.getHttpServer())
      .post('/auth/register')
      .send({ ...conta('-y'), password: 'curta' })
      .expect(400);

    expect(JSON.stringify(res.body)).toMatch(/mínimo 8 caracteres/i);
    // BE-4: a senha rejeitada não pode voltar na resposta de erro.
    expect(JSON.stringify(res.body)).not.toContain('curta');
  });

  it('recusa campo não previsto no contrato', async () => {
    await request(app.getHttpServer())
      .post('/auth/register')
      .send({ ...conta('-z'), admin: true })
      .expect(400);
  });

  it('normaliza e-mail para minúsculas', async () => {
    const res = await request(app.getHttpServer())
      .post('/auth/register')
      .send({ ...conta('-Maiusc'), email: 'PARTICIPANTE-MAIUSC@e2e.teste' })
      .expect(201);

    expect((res.body as AccountResponse).email).toBe(
      'participante-maiusc@e2e.teste',
    );
  });
});
