import { ProductosService } from './productos.service';

/**
 * Filtro del catálogo por rubro. Ver docs/plan-rubros-multi-sucursal.md.
 *
 * El producto no lleva rubro propio: lo hereda de su categoría. Un producto sin
 * categoría, o con una categoría sin rubro, es compartido y se ve en todos.
 */
describe('ProductosService.findAll - filtro por rubro', () => {
  const crearService = () => {
    const prisma: any = {
      productos: { findMany: jest.fn().mockResolvedValue([]), count: jest.fn().mockResolvedValue(0) },
      depositos: { findMany: jest.fn().mockResolvedValue([]) },
    };
    prisma.$transaction = jest.fn().mockResolvedValue([[], 0]);
    const service = new ProductosService(prisma, { get: () => undefined } as any, {} as any, {} as any, {} as any);
    return { service, prisma };
  };

  const whereDeLaConsulta = (prisma: any) => prisma.$transaction.mock.calls[0][0][0];

  it('sin rubro no agrega filtro de categoría', async () => {
    const { service, prisma } = crearService();

    await service.findAll(1, 10, 'emp-1');

    expect(whereDeLaConsulta(prisma)).toBeDefined();
  });

  it('con rubro respeta el override del producto y, si no hay, su categoría', async () => {
    const { service, prisma } = crearService();
    let whereCapturado: any;
    prisma.productos.findMany = jest.fn().mockImplementation((args: any) => {
      whereCapturado = args.where;
      return Promise.resolve([]);
    });

    await service.findAll(1, 10, 'emp-1', undefined, false, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 'rub-desp');

    expect(whereCapturado.AND).toEqual([
      {
        OR: [
          { rubro_id: 'rub-desp' },
          {
            rubro_id: null,
            OR: [
              { categoria_id: null },
              { categoria: { rubro_id: null } },
              { categoria: { rubro_id: 'rub-desp' } },
            ],
          },
        ],
      },
    ]);
  });

  it('el filtro de rubro no pisa el OR de la búsqueda por texto', async () => {
    const { service, prisma } = crearService();
    let whereCapturado: any;
    prisma.productos.findMany = jest.fn().mockImplementation((args: any) => {
      whereCapturado = args.where;
      return Promise.resolve([]);
    });

    await service.findAll(1, 10, 'emp-1', undefined, false, 'tomate', undefined, undefined, undefined, undefined, undefined, undefined, undefined, 'rub-desp');

    // La búsqueda por descripción/código/código de barra sigue viva.
    expect(whereCapturado.OR).toHaveLength(3);
    expect(whereCapturado.AND).toHaveLength(1);
  });
});

/**
 * La búsqueda del POS (`/productos/search`) usa el mismo filtro: sin él, el POS
 * de una sucursal encontraba por nombre o código productos de otro rubro.
 */
describe('ProductosService.search - filtro por rubro', () => {
  // `asignaciones` = sucursales del usuario con sus rubros. Vacío = sin
  // restricción (usuario sin asignaciones).
  const crearService = (asignaciones: Array<{ sucursal: { sucursal_rubros: { rubro_id: string }[] } }> = []) => {
    const prisma: any = {
      productos: { findMany: jest.fn().mockResolvedValue([]) },
      depositos: { findFirst: jest.fn().mockResolvedValue({ id: 'dep-1' }) },
      usuario: { findUnique: jest.fn().mockResolvedValue({ is_superadmin: false }) },
      empresas: { findUnique: jest.fn().mockResolvedValue({ type: 'subsidiary' }) },
      asignacion_sucursal_caja_usuario: { findMany: jest.fn().mockResolvedValue(asignaciones) },
    };
    const service = new ProductosService(prisma, { get: () => undefined } as any, {} as any, {} as any, {} as any);
    return { service, prisma };
  };
  const where = (prisma: any) => prisma.productos.findMany.mock.calls[0][0].where;

  it('con rubro filtra y conserva la búsqueda por texto', async () => {
    const { service, prisma } = crearService();
    await service.search('vincha', 'emp-1', 20, undefined, undefined, 'usr-1', false, undefined, 'rub-taller');

    expect(where(prisma).OR.length).toBeGreaterThan(0);
    expect(where(prisma).AND).toEqual([
      {
        OR: [
          { rubro_id: 'rub-taller' },
          {
            rubro_id: null,
            OR: [
              { categoria_id: null },
              { categoria: { rubro_id: null } },
              { categoria: { rubro_id: 'rub-taller' } },
            ],
          },
        ],
      },
    ]);
  });

  it('sin rubro busca en todo el catálogo, como antes', async () => {
    const { service, prisma } = crearService();
    await service.search('vincha', 'emp-1', 20);
    expect(where(prisma).AND).toBeUndefined();
  });

  const delTaller = [{ sucursal: { sucursal_rubros: [{ rubro_id: 'rub-taller' }] } }];

  it('un usuario restringido al taller queda recortado aunque no mande rubro', async () => {
    const { service, prisma } = crearService(delTaller);
    await service.search('vincha', 'emp-1', 20, undefined, undefined, 'usr-1');
    expect(JSON.stringify(where(prisma).AND)).toContain('rub-taller');
  });

  it('un usuario restringido al taller que pide otro rubro no encuentra nada', async () => {
    const { service, prisma } = crearService(delTaller);
    await service.search('vincha', 'emp-1', 20, undefined, undefined, 'usr-1', false, undefined, 'rub-regalos');
    expect(where(prisma).AND).toEqual([{ id: { in: [] } }]);
  });
});
