import { BadRequestException } from '@nestjs/common';
import { RequisicionesService } from './requisiciones.service';

describe('RequisicionesService - reglas F1 sin presupuesto operativo', () => {
  const createService = (prisma: any = {}) =>
    new RequisicionesService(prisma as any, {} as any, {} as any);

  describe('normalizeItems', () => {
    const txBase = () => ({
      cont_centros_costo: {
        findFirst: jest.fn().mockResolvedValue({ id: 'cc-1' }),
      },
      productos: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'prod-1',
          precio_costo: 1500,
          precio: 1800,
          porcentaje_iva: 10,
        }),
      },
      tipo_gasto: {
        findFirst: jest.fn().mockResolvedValue({ id: 'tg-1' }),
      },
    });

    it('en origen compra exige producto o descripción', async () => {
      const service = createService();
      const normalizeItems = (service as any).normalizeItems.bind(service);
      const tx = txBase();

      await expect(
        normalizeItems(tx, 'emp-1', 'compra', [
          {
            centro_costo_id: 'cc-1',
            producto_id: null,
            tipo_gasto_id: null,
            descripcion: '   ',
            cantidad_solicitada: 1,
          },
        ]),
      ).rejects.toThrow(BadRequestException);
    });

    it('usa costo referencial del producto cuando no se envía precio', async () => {
      const service = createService();
      const normalizeItems = (service as any).normalizeItems.bind(service);
      const tx = txBase();

      const output = await normalizeItems(tx, 'emp-1', 'compra', [
        {
          centro_costo_id: 'cc-1',
          producto_id: 'prod-1',
          descripcion: 'Producto prueba',
          cantidad_solicitada: 3,
        },
      ]);

      expect(output).toEqual([
        expect.objectContaining({
          precio_unitario: 1500,
          porcentaje_iva: 10,
          subtotal_estimado: 4500,
        }),
      ]);
    });

    it('respeta precio enviado explícitamente', async () => {
      const service = createService();
      const normalizeItems = (service as any).normalizeItems.bind(service);
      const tx = txBase();

      const output = await normalizeItems(tx, 'emp-1', 'compra', [
        {
          centro_costo_id: 'cc-1',
          producto_id: 'prod-1',
          descripcion: 'Producto prueba',
          cantidad_solicitada: 2,
          precio_unitario: 999,
          porcentaje_iva: 5,
        },
      ]);

      expect(output).toEqual([
        expect.objectContaining({
          precio_unitario: 999,
          porcentaje_iva: 5,
          subtotal_estimado: 1998,
        }),
      ]);
    });
  });

  describe('enviarAprobacion', () => {
    it('en compra con ítem libre usa solo nivel 1 y no bloquea', async () => {
      const tx = {
        config_compras: {
          findUnique: jest.fn().mockResolvedValue({ habilitar_requisicion_compra: true }),
          create: jest.fn(),
        },
        requisicion_cab: {
          findFirst: jest.fn().mockResolvedValue({
            id: 'req-1',
            empresa_id: 'emp-1',
            estado: 'borrador',
            origen_tipo: 'compra',
            total_estimado: 0,
            requisicion_det: [{ id: 'det-1', producto_id: null }],
          }),
          update: jest.fn().mockResolvedValue(undefined),
        },
        requisicion_aprobacion_config: {
          findMany: jest.fn().mockResolvedValue([
            {
              id: 'cfg-n1',
              nivel: 1,
              perfil_id: 'perfil-1',
            },
          ]),
        },
        requisicion_aprobacion: {
          deleteMany: jest.fn().mockResolvedValue(undefined),
          createMany: jest.fn().mockResolvedValue(undefined),
        },
        requisicion_historial: {
          create: jest.fn().mockResolvedValue(undefined),
        },
      };

      const prisma = {
        $transaction: jest.fn().mockImplementation(async (cb: any) => cb(tx)),
      };

      const service = createService(prisma);
      jest.spyOn(service, 'findOne').mockResolvedValue({ id: 'req-1' } as any);

      await expect(service.enviarAprobacion('req-1', 'emp-1', 'usr-1')).resolves.toEqual({ id: 'req-1' });
      expect(tx.requisicion_aprobacion_config.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({ nivel: 1, origen_tipo: 'compra' }),
        }),
      );
      expect(tx.requisicion_aprobacion.createMany).toHaveBeenCalledWith(
        expect.objectContaining({
          data: [
            expect.objectContaining({
              nivel: 1,
              config_id: 'cfg-n1',
            }),
          ],
        }),
      );
    });
  });

  describe('aprobar', () => {
    it('aprueba nivel final sin usar presupuesto operativo', async () => {
      const tx = {
        config_compras: {
          findUnique: jest.fn().mockResolvedValue({ habilitar_requisicion_compra: true }),
          create: jest.fn(),
        },
        requisicion_cab: {
          findFirst: jest.fn().mockResolvedValue({
            id: 'req-1',
            empresa_id: 'emp-1',
            estado: 'pendiente_aprobacion',
            requisicion_det: [{ id: 'det-1', subtotal_estimado: 1000 }],
            requisicion_aprobacion: [
              {
                id: 'ap-1',
                nivel: 1,
                perfil_id: 'perfil-aprobador',
                estado: 'pendiente',
              },
            ],
          }),
          update: jest.fn().mockResolvedValue(undefined),
        },
        requisicion_aprobacion: {
          update: jest.fn().mockResolvedValue(undefined),
        },
        requisicion_historial: {
          create: jest.fn().mockResolvedValue(undefined),
        },
        usuario: {
          findFirst: jest.fn().mockResolvedValue({ is_superadmin: false }),
        },
        usuario_perfiles: {
          findFirst: jest
            .fn()
            .mockResolvedValueOnce({ id: 'perm-compras-editar' })
            .mockResolvedValueOnce({ id: 'perfil-nivel' }),
        },
      };

      const prisma = {
        $transaction: jest.fn().mockImplementation(async (cb: any) => cb(tx)),
      };
      const service = createService(prisma);
      jest.spyOn(service, 'findOne').mockResolvedValue({ id: 'req-1' } as any);

      await expect(service.aprobar('req-1', 'emp-1', 'usr-1')).resolves.toEqual({ id: 'req-1' });
      expect(tx.requisicion_cab.update).toHaveBeenCalled();
    });
  });

  describe('findAll sanitización', () => {
    it('oculta montos para usuario sin permiso COMPRAS+EDITAR', async () => {
      const prisma = {
        $transaction: jest.fn().mockResolvedValue([
          [
            {
              id: 'req-1',
              total_estimado: 5000,
              total_ejecutado: 1200,
              requisicion_det: [
                {
                  precio_unitario: 1000,
                  subtotal_estimado: 5000,
                  monto_ejecutado: 1200,
                },
              ],
            },
          ],
          1,
        ]),
        requisicion_cab: {
          findMany: jest.fn(),
          count: jest.fn(),
        },
        usuario: {
          findFirst: jest.fn().mockResolvedValue({ is_superadmin: false }),
        },
        usuario_perfiles: {
          findFirst: jest.fn().mockResolvedValue(null),
        },
      };

      const service = createService(prisma);
      const result = await service.findAll('emp-1', 'usr-1', { page: '1', limit: '20' });
      const row = result.data[0];

      expect(row.total_estimado).toBeNull();
      expect(row.total_ejecutado).toBeNull();
      expect(row.requisicion_det[0].precio_unitario).toBeNull();
      expect(row.requisicion_det[0].subtotal_estimado).toBeNull();
      expect(row.requisicion_det[0].monto_ejecutado).toBeNull();
    });
  });
});
