import { ReversionRemisionService } from './reversion-remision.service';

/**
 * Deshacer una remisión (anulación manual o ECAN/EINU aprobado por SIFEN):
 * repone el stock que descontó, libera lo aplicado a facturas y la deja Anulada.
 */
describe('ReversionRemisionService.revertirRemision', () => {
  const crearService = ({
    marcadas = 1,
    aplicaciones = [] as Array<{ factura_det_id: string; cantidad_aplicada: number }>,
    movimientos = [] as Array<Record<string, unknown>>,
  } = {}) => {
    const tx: any = {
      nota_remision_cab: {
        updateMany: jest.fn().mockResolvedValue({ count: marcadas }),
        findUnique: jest.fn().mockResolvedValue({ dest: '001', dpunexp: '002', dnumdoc: '0000010' }),
      },
      nota_remision_det: { findMany: jest.fn().mockResolvedValue([{ id: 'det-1' }, { id: 'det-2' }]) },
      nota_remision_det_aplicacion: { findMany: jest.fn().mockResolvedValue(aplicaciones) },
      movimientos_inventario: {
        findMany: jest.fn().mockResolvedValue(movimientos),
        create: jest.fn().mockResolvedValue({}),
      },
      tipo_movimiento_inventario: { findFirst: jest.fn().mockResolvedValue({ id: 'tipo-entrada' }) },
      stock_deposito: {
        findUnique: jest.fn().mockResolvedValue({ cantidad_disponible: 5 }),
        upsert: jest.fn().mockResolvedValue({}),
      },
      $executeRaw: jest.fn().mockResolvedValue(1),
    };
    const prisma: any = { $transaction: jest.fn((fn: (t: any) => unknown) => fn(tx)) };
    return { service: new ReversionRemisionService(prisma), tx };
  };

  it('repone el stock descontado y libera lo aplicado a facturas', async () => {
    const { service, tx } = crearService({
      aplicaciones: [
        { factura_det_id: 'fd-1', cantidad_aplicada: 2 },
        { factura_det_id: 'fd-1', cantidad_aplicada: 3 },
      ],
      movimientos: [{ deposito_origen_id: 'dep-1', producto_id: 'prod-1', cantidad: 4, precio_unitario: 100 }],
    });

    const res = await service.revertirRemision('rem-1', 'emp-1');

    expect(res).toEqual({ revertida: true, stockRepuesto: 1 });
    expect(tx.nota_remision_cab.updateMany).toHaveBeenCalledWith(
      expect.objectContaining({
        where: expect.objectContaining({ id: 'rem-1', empresa_id: 'emp-1' }),
        data: expect.objectContaining({ estado: 'Anulado' }),
      }),
    );
    // Un solo UPDATE por factura_det con los decrementos sumados.
    expect(tx.$executeRaw).toHaveBeenCalledTimes(1);
    expect(tx.$executeRaw.mock.calls[0]).toContain(5);
    expect(tx.stock_deposito.upsert).toHaveBeenCalledWith(
      expect.objectContaining({ update: expect.objectContaining({ cantidad_disponible: 9 }) }),
    );
    expect(tx.movimientos_inventario.create).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({ documento_origen: 'nota_remision_anulacion', documento_id: 'rem-1', cantidad: 4 }),
      }),
    );
  });

  it('si ya estaba anulada no revierte de nuevo (anulación manual + evento aprobado después)', async () => {
    const { service, tx } = crearService({
      marcadas: 0,
      aplicaciones: [{ factura_det_id: 'fd-1', cantidad_aplicada: 2 }],
      movimientos: [{ deposito_origen_id: 'dep-1', producto_id: 'prod-1', cantidad: 4, precio_unitario: 100 }],
    });

    const res = await service.revertirRemision('rem-1', 'emp-1');

    expect(res).toEqual({ revertida: false, stockRepuesto: 0 });
    expect(tx.$executeRaw).not.toHaveBeenCalled();
    expect(tx.stock_deposito.upsert).not.toHaveBeenCalled();
    expect(tx.movimientos_inventario.create).not.toHaveBeenCalled();
  });
});
