import { NotFoundException } from '@nestjs/common';
import { ProveedoresService } from './proveedores.service';

/**
 * Filtro por rubro y asignación de rubros. Ver docs/plan-rubros-multi-sucursal.md.
 */
describe('ProveedoresService - rubros', () => {
  const crearService = (overrides: Record<string, any> = {}) => {
    const prisma: any = {
      proveedores: {
        findFirst: jest.fn().mockResolvedValue(null),
        findMany: jest.fn().mockResolvedValue([]),
        count: jest.fn().mockResolvedValue(0),
      },
      rubros: { findMany: jest.fn().mockResolvedValue([]) },
      proveedor_rubros: {
        deleteMany: jest.fn().mockResolvedValue({ count: 0 }),
        createMany: jest.fn().mockResolvedValue({ count: 0 }),
      },
      ...overrides,
    };
    prisma.$transaction = jest.fn().mockImplementation((arg: any) =>
      typeof arg === 'function' ? arg(prisma) : Promise.all(arg),
    );
    const personas = {};
    const audit = { log: jest.fn().mockResolvedValue(undefined) };
    return { service: new ProveedoresService(prisma as any, personas as any, audit as any), prisma };
  };

  describe('search', () => {
    it('sin rubro no agrega ningún filtro extra', async () => {
      const { service, prisma } = crearService();

      await service.search('nuar', 'emp-1', 20);

      expect(prisma.proveedores.findMany.mock.calls[0][0].where.AND).toBeUndefined();
    });

    it('con rubro filtra por rubro sin romper la búsqueda por texto', async () => {
      const { service, prisma } = crearService();

      await service.search('nuar', 'emp-1', 20, { rubro_id: 'rub-desp' });

      const where = prisma.proveedores.findMany.mock.calls[0][0].where;
      // La búsqueda por texto sigue viva en OR...
      expect(where.OR).toHaveLength(3);
      // ...y el rubro va aparte, bajo AND.
      expect(where.AND).toEqual([
        { OR: [{ proveedor_rubros: { none: {} } }, { proveedor_rubros: { some: { rubro_id: 'rub-desp' } } }] },
      ]);
    });
  });

  describe('findAll', () => {
    it('con rubro filtra por rubro', async () => {
      const { service, prisma } = crearService();

      await service.findAll(1, 10, 'emp-1', undefined, { rubro_id: 'rub-desp' });

      const where = prisma.proveedores.findMany.mock.calls[0][0].where;
      expect(where.AND).toEqual([
        { OR: [{ proveedor_rubros: { none: {} } }, { proveedor_rubros: { some: { rubro_id: 'rub-desp' } } }] },
      ]);
    });
  });

  describe('setRubros', () => {
    it('reemplaza el set completo de rubros del proveedor', async () => {
      const { service, prisma } = crearService({
        proveedores: { findFirst: jest.fn().mockResolvedValue({ id: 'prov-1', empresa_id: 'emp-1' }) },
        rubros: { findMany: jest.fn().mockResolvedValue([{ id: 'rub-1' }, { id: 'rub-2' }]) },
      });

      await service.setRubros('prov-1', ['rub-1', 'rub-2'], 'emp-1', 'user-1');

      expect(prisma.proveedor_rubros.deleteMany).toHaveBeenCalledWith({ where: { proveedor_id: 'prov-1' } });
      expect(prisma.proveedor_rubros.createMany.mock.calls[0][0].data).toEqual([
        { proveedor_id: 'prov-1', rubro_id: 'rub-1' },
        { proveedor_id: 'prov-1', rubro_id: 'rub-2' },
      ]);
    });

    it('lista vacía deja al proveedor visible en todos los rubros', async () => {
      const { service, prisma } = crearService({
        proveedores: { findFirst: jest.fn().mockResolvedValue({ id: 'prov-1', empresa_id: 'emp-1' }) },
      });

      await service.setRubros('prov-1', [], 'emp-1');

      expect(prisma.proveedor_rubros.deleteMany).toHaveBeenCalledWith({ where: { proveedor_id: 'prov-1' } });
      expect(prisma.proveedor_rubros.createMany).not.toHaveBeenCalled();
    });

    it('rechaza rubros de otra empresa', async () => {
      const { service } = crearService({
        proveedores: { findFirst: jest.fn().mockResolvedValue({ id: 'prov-1', empresa_id: 'emp-1' }) },
        rubros: { findMany: jest.fn().mockResolvedValue([{ id: 'rub-1' }]) },
      });

      await expect(service.setRubros('prov-1', ['rub-1', 'rub-ajeno'], 'emp-1')).rejects.toThrow(NotFoundException);
    });

    it('no deja tocar un proveedor de otra empresa', async () => {
      const { service } = crearService();

      await expect(service.setRubros('prov-ajeno', ['rub-1'], 'emp-1')).rejects.toThrow(NotFoundException);
    });
  });
});
