import { NumeracionCrmService } from './numeracion-crm.service';

describe('NumeracionCrmService', () => {
  const crear = (fila: { id?: string; prefijo?: string; numero_actual?: number; relleno?: number } = {}) => {
    const cliente: any = {
      crm_numeracion: {
        createMany: jest.fn().mockResolvedValue({ count: 0 }),
        update: jest.fn().mockImplementation(({ data }) => Promise.resolve(data)),
      },
      $queryRaw: jest.fn().mockResolvedValue([
        { id: fila.id ?? 'num-1', prefijo: fila.prefijo ?? 'OPP', numero_actual: fila.numero_actual ?? 0, relleno: fila.relleno ?? 7 },
      ]),
    };
    const prisma: any = { ...cliente, $transaction: jest.fn().mockImplementation((cb: any) => cb(cliente)) };
    return { service: new NumeracionCrmService(prisma as any), prisma, cliente };
  };

  it('el primer número de una empresa es el 1, con el prefijo y el relleno', async () => {
    const { service } = crear({ numero_actual: 0 });

    expect(await service.siguiente('emp-1', 'OPP')).toBe('OPP-0000001');
  });

  it('avanza el contador', async () => {
    const { service, cliente } = crear({ numero_actual: 123 });

    expect(await service.siguiente('emp-1', 'OPP')).toBe('OPP-0000124');
    expect(cliente.crm_numeracion.update.mock.calls[0][0].data.numero_actual).toBe(124);
  });

  it('crea la fila sola la primera vez, sin romper si ya existe', async () => {
    const { service, cliente } = crear();

    await service.siguiente('emp-1', 'TCK');

    expect(cliente.crm_numeracion.createMany).toHaveBeenCalledWith({
      data: [{ empresa_id: 'emp-1', tipo: 'TCK', prefijo: 'TCK' }],
      skipDuplicates: true,
    });
  });

  it('respeta un relleno distinto', async () => {
    const { service } = crear({ prefijo: 'CTR', numero_actual: 8, relleno: 4 });

    expect(await service.siguiente('emp-1', 'CTR')).toBe('CTR-0009');
  });

  it('bloquea la fila con FOR UPDATE: sin eso dos vendedores se llevan el mismo número', async () => {
    const { service, cliente } = crear();

    await service.siguiente('emp-1', 'OPP');

    const sql = cliente.$queryRaw.mock.calls[0][0].join('?');
    expect(sql).toContain('FOR UPDATE');
    expect(sql).toContain('crm_numeracion');
  });

  it('con una transacción del llamador no abre otra: el número vive y muere con ella', async () => {
    const { service, prisma, cliente } = crear();

    await service.siguiente('emp-1', 'OPP', cliente as any);

    expect(prisma.$transaction).not.toHaveBeenCalled();
  });

  it('sin transacción del llamador abre la suya', async () => {
    const { service, prisma } = crear();

    await service.siguiente('emp-1', 'OPP');

    expect(prisma.$transaction).toHaveBeenCalled();
  });
});
