/**
 * E2E auth — nécessite l'infra locale (pnpm infra:up) : base seedée au beforeAll.
 * DEMO_MODE est activé AVANT la composition d'AppModule (voir demo-off.e2e-spec
 * pour l'état inverse : les routes démo doivent répondre 404).
 */
process.env.DEMO_MODE = 'true';

import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import request from 'supertest';
import { seed } from '../prisma/seed';
import { AppModule } from '../src/app.module';

describe('Auth (e2e, DEMO_MODE=true)', () => {
  let app: INestApplication;
  let http: () => request.Agent;
  const prisma = new PrismaClient();

  beforeAll(async () => {
    await seed(prisma);
    const moduleRef = await Test.createTestingModule({
      imports: [AppModule.forRoot()],
    }).compile();
    app = moduleRef.createNestApplication();
    await app.init();
    http = () => request(app.getHttpServer());
  });

  afterAll(async () => {
    await app?.close();
    await prisma.$disconnect();
  });

  it('GET /health est publique', async () => {
    const res = await http().get('/health').expect(200);
    expect(res.body.services.database).toBe('up');
  });

  it('l’API est fermée par défaut (401 sans jeton)', async () => {
    await http().get('/users/me').expect(401);
  });

  it('GET /auth/demo-accounts liste les 7 comptes seedés, sans secret', async () => {
    const res = await http().get('/auth/demo-accounts').expect(200);
    expect(res.body.accounts).toHaveLength(7);
    for (const account of res.body.accounts) {
      expect(Object.keys(account).sort()).toEqual([
        'displayName',
        'id',
        'initials',
        'roleName',
      ]);
    }
  });

  it('demo-login émet un jeton utilisable sur /users/me (critère < 3 s)', async () => {
    const start = Date.now();
    const { body: accounts } = await http().get('/auth/demo-accounts');
    const admin = accounts.accounts.find(
      (a: { roleName: string }) => a.roleName === 'Administrateur',
    );
    const login = await http()
      .post('/auth/demo-login')
      .send({ userId: admin.id })
      .expect(200);
    const me = await http()
      .get('/users/me')
      .set('Authorization', `Bearer ${login.body.accessToken}`)
      .expect(200);
    expect(Date.now() - start).toBeLessThan(3000);
    expect(me.body.role.name).toBe('Administrateur');
    expect(me.body.isDemo).toBe(true);
    // La matrice complète accompagne le profil (une ligne par catégorie)
    expect(me.body.permissions).toHaveLength(10);
  });

  it('demo-login refuse un compte isDemo=false (403)', async () => {
    const role = await prisma.role.findUniqueOrThrow({
      where: { name: 'Administrateur' },
    });
    const real = await prisma.user.upsert({
      where: { email: 'real@spelev.ma' },
      update: {},
      create: {
        email: 'real@spelev.ma',
        displayName: 'Compte Réel',
        roleId: role.id,
        isDemo: false,
      },
    });
    try {
      await http().post('/auth/demo-login').send({ userId: real.id }).expect(403);
    } finally {
      await prisma.user.delete({ where: { id: real.id } });
    }
  });

  it('demo-login sur un id inconnu → 404', async () => {
    await http()
      .post('/auth/demo-login')
      .send({ userId: '00000000-0000-4000-8000-000000000000' })
      .expect(404);
  });

  it('la connexion classique fonctionne (seed) et rejette un mauvais mot de passe', async () => {
    const password = process.env.SEED_DEMO_PASSWORD ?? 'Demo!2026';
    const ok = await http()
      .post('/auth/login')
      .send({ email: 'dispatcher@demo.siop.ma', password })
      .expect(200);
    expect(ok.body.user.role.name).toBe('Dispatcher');

    await http()
      .post('/auth/login')
      .send({ email: 'dispatcher@demo.siop.ma', password: 'mauvais' })
      .expect(401);
  });

  it('valide les corps de requête via le contrat Zod (400)', async () => {
    await http().post('/auth/demo-login').send({ userId: 'pas-un-uuid' }).expect(400);
    await http().post('/auth/login').send({ email: 'x' }).expect(400);
  });
});
