import { RankingService } from './ranking.service';
import type { PrismaService } from '../prisma/prisma.service';

/**
 * Execução do caso `PR-3-11` do plano `docs/testes/PR-3-plano.md`: montar o
 * ranking não pode custar uma consulta por participante.
 *
 * O jeito honesto de verificar isso é **contar chamadas ao banco** com o número
 * de participantes variando. Asserir o resultado do ranking não pega N+1: a
 * lista sai igual, só mais devagar — e em bolão de empresa é a diferença entre
 * abrir a tela e desistir dela.
 */
describe('Plano PR-3 — ranking sem N+1', () => {
  const montar = (quantosMembros: number) => {
    let chamadas = 0;
    const conta = <T>(valor: T) => {
      chamadas++;
      return Promise.resolve(valor);
    };

    const membros = Array.from({ length: quantosMembros }, (_, i) => ({
      userId: `u${i}`,
      user: { displayName: `P${i}` },
    }));

    const prisma = {
      poolMember: {
        findMany: () => conta(membros),
        // Usado pela checagem de participação, uma vez por requisição.
        findUnique: () => conta({ leftAt: null }),
      },
      prediction: {
        groupBy: () =>
          conta(
            membros.map((m) => ({
              userId: m.userId,
              band: 'PLACAR_EXATO',
              _sum: { points: 10 },
              _count: { _all: 1 },
            })),
          ),
      },
      pool: {
        findUnique: () => conta({ tiebreakOrder: ['EXACT_SCORES', 'WINNERS'] }),
      },
      // Usado só pelo ranking da rodada, para dizer se a apuração está parcial.
      poolMatch: { count: () => conta(10) },
    } as unknown as PrismaService;

    return { service: new RankingService(prisma), quantasChamadas: () => chamadas };
  };

  it('PR-3-11 o número de consultas não cresce com o número de participantes', async () => {
    const pequeno = montar(3);
    await pequeno.service.geral('p1', 'u0');
    const comTres = pequeno.quantasChamadas();

    const grande = montar(60);
    await grande.service.geral('p1', 'u0');
    const comSessenta = grande.quantasChamadas();

    // Mesmo custo: uma leitura de membros (com o nome vindo no join), uma
    // agregação e as checagens fixas da requisição.
    expect(comSessenta).toBe(comTres);
    // E é um número pequeno — se virar dezenas, alguém acrescentou consulta por
    // linha sem perceber.
    expect(comTres).toBeLessThanOrEqual(4);
  });

  it('PR-4-10 o ranking da rodada também não cresce com a galera', async () => {
    // Rota diferente, mesmo risco. Ela ainda conta jogos apurados para dizer se
    // a rodada está parcial, então o número absoluto é maior — o que importa é
    // continuar **constante** em relação ao número de participantes.
    const pequeno = montar(3);
    await pequeno.service.daRodada('p1', 20, 'u0');
    const comTres = pequeno.quantasChamadas();

    const grande = montar(60);
    await grande.service.daRodada('p1', 20, 'u0');

    expect(grande.quantasChamadas()).toBe(comTres);
    expect(comTres).toBeLessThanOrEqual(6);
  });
});
