import { BadRequestException } from '@nestjs/common';
import { PagosProveedorService } from './pagos-proveedor.service';

describe('PagosProveedorService - normalizeConfigCompras', () => {
  const createService = () =>
    new PagosProveedorService({} as any, {} as any);

  const baseConfig = () => ({
    habilitar_requisicion_compra: true,
    modo_flujo_compra: 'directo',
    afectar_stock_en_compra_directa: true,
    habilitar_orden_compra: true,
    habilitar_recepcion_compra: true,
    habilitar_matching_3_vias: true,
    habilitar_orden_pago: true,
    requiere_aprobacion: false,
    permite_pago_sin_aprobacion: true,
    generar_cxp_automatico: true,
    permitir_edicion_cxp: true,
    permitir_pago_parcial: false,
    usar_workflow: false,
    workflow_obligatorio: false,
    aplicar_pago_al_guardar: true,
  });

  it('fuerza modo directo cuando requisición está deshabilitada', () => {
    const service = createService();
    const normalizeConfigCompras = (service as any).normalizeConfigCompras.bind(service);

    const output = normalizeConfigCompras({
      ...baseConfig(),
      habilitar_requisicion_compra: false,
      modo_flujo_compra: 'requisicion_obligatoria',
    });

    expect(output.habilitar_requisicion_compra).toBe(false);
    expect(output.modo_flujo_compra).toBe('directo');
  });

  it('en modo requisición obligatoria fuerza requisición y orden de compra habilitadas', () => {
    const service = createService();
    const normalizeConfigCompras = (service as any).normalizeConfigCompras.bind(service);

    const output = normalizeConfigCompras({
      ...baseConfig(),
      modo_flujo_compra: 'requisicion_obligatoria',
      habilitar_requisicion_compra: true,
      habilitar_orden_compra: false,
    });

    expect(output.modo_flujo_compra).toBe('requisicion_obligatoria');
    expect(output.habilitar_requisicion_compra).toBe(true);
    expect(output.habilitar_orden_compra).toBe(true);
  });

  it('si orden de compra está deshabilitada, conserva recepción libre y deshabilita matching', () => {
    const service = createService();
    const normalizeConfigCompras = (service as any).normalizeConfigCompras.bind(service);

    const output = normalizeConfigCompras({
      ...baseConfig(),
      habilitar_orden_compra: false,
      habilitar_recepcion_compra: true,
      habilitar_matching_3_vias: true,
    });

    expect(output.habilitar_orden_compra).toBe(false);
    expect(output.habilitar_recepcion_compra).toBe(true);
    expect(output.habilitar_matching_3_vias).toBe(false);
  });

  it('si workflow es obligatorio, fuerza usar_workflow y requiere_aprobacion', () => {
    const service = createService();
    const normalizeConfigCompras = (service as any).normalizeConfigCompras.bind(service);

    const output = normalizeConfigCompras({
      ...baseConfig(),
      workflow_obligatorio: true,
      usar_workflow: false,
      requiere_aprobacion: false,
    });

    expect(output.workflow_obligatorio).toBe(true);
    expect(output.usar_workflow).toBe(true);
    expect(output.requiere_aprobacion).toBe(true);
  });

  it('rechaza configuración inválida cuando stock directo está deshabilitado y recepción también', () => {
    const service = createService();
    const normalizeConfigCompras = (service as any).normalizeConfigCompras.bind(service);

    expect(() =>
      normalizeConfigCompras({
        ...baseConfig(),
        afectar_stock_en_compra_directa: false,
        habilitar_recepcion_compra: false,
      }),
    ).toThrow(BadRequestException);
  });
});

describe('PagosProveedorService - ejecución/anulación F5/F6', () => {
  const createService = (txOverrides: Record<string, any> = {}) => {
    const tx = {
      config_compras: {
        findUnique: jest.fn().mockResolvedValue({
          empresa_id: 'emp-1',
          habilitar_orden_pago: true,
        }),
        create: jest.fn(),
      },
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          usuario_id: 'u-1',
          moneda_id: 'mon-1',
          cotizacion: 1,
          numero_orden_pago: 'OP-0001',
          fecha_pago: new Date('2026-04-27'),
          referencia: null,
          observaciones: null,
          estado: 'BORRADOR',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [
            {
              cuenta_pagar_id: 'cxp-1',
              monto: 500,
              monto_moneda: 500,
              cotizacion: 1,
              medio_pago_id: null,
              referencia: null,
              observaciones: null,
            },
          ],
        }),
        update: jest.fn().mockResolvedValue(undefined),
      },
      orden_pago_proveedor_det: {
        findMany: jest.fn(),
      },
      cuentas_pagar: {
        findMany: jest.fn().mockResolvedValue([
          {
            id: 'cxp-1',
            compra_id: 'comp-1',
            origen_tipo: 'compra',
            origen_id: 'comp-1',
            moneda_id: 'mon-1',
            saldo_pendiente: 500,
          },
        ]),
        findUnique: jest.fn().mockResolvedValue({
          id: 'cxp-1',
          compra_id: null,
          origen_tipo: 'compra',
          origen_id: 'comp-1',
          monto_pagado: 500,
          saldo_pendiente: 0,
          monto_original: 500,
        }),
        update: jest.fn().mockResolvedValue(undefined),
      },
      pagos_proveedor: {
        create: jest.fn().mockResolvedValue(undefined),
        updateMany: jest.fn().mockResolvedValue({ count: 1 }),
      },
      tes_reglas: {
        findFirst: jest.fn().mockResolvedValue(null),
      },
      tes_cuentas: {
        findFirst: jest.fn().mockResolvedValue(null),
        update: jest.fn().mockResolvedValue(undefined),
      },
      tes_movimientos: {
        findFirst: jest.fn().mockResolvedValue(null),
        findMany: jest.fn().mockResolvedValue([]),
        create: jest.fn().mockResolvedValue({ id: 'tes-mov-1' }),
        updateMany: jest.fn().mockResolvedValue({ count: 0 }),
      },
      tes_movimiento_det: {
        create: jest.fn().mockResolvedValue(undefined),
      },
      tes_categorias: {
        findFirst: jest.fn().mockResolvedValue({ id: 'cat-egreso-otro' }),
        create: jest.fn().mockResolvedValue({ id: 'cat-egreso-otro' }),
      },
      ...txOverrides,
    };

    const prisma = {
      $transaction: jest.fn(async (cb: any) => cb(tx)),
      orden_pago_proveedor_cab: tx.orden_pago_proveedor_cab,
      tes_reglas: tx.tes_reglas,
      tes_cuentas: tx.tes_cuentas,
      tes_movimientos: tx.tes_movimientos,
      tes_movimiento_det: tx.tes_movimiento_det,
      tes_categorias: tx.tes_categorias,
    };
    const contabilidadIntegracion = {
      integrarOrdenPago: jest.fn().mockResolvedValue(undefined),
      revertirDocumento: jest.fn().mockResolvedValue(undefined),
    };
    const service = new PagosProveedorService(prisma as any, contabilidadIntegracion as any);

    jest.spyOn(service as any, 'recalculateCompraEstado').mockResolvedValue(undefined);
    jest.spyOn(service as any, 'recalculateAutoOrderEstado').mockResolvedValue(undefined);
    jest.spyOn(service as any, 'recalculateGastoEstado').mockResolvedValue(undefined);

    return { service, tx, contabilidadIntegracion };
  };

  it('ejecutar pasa OP de BORRADOR a PAGADO y liquida CxP exacta', async () => {
    const { service, tx, contabilidadIntegracion } = createService();

    await expect(service.ejecutar('op-1', 'emp-1', 'u-1')).resolves.toEqual({
      message: 'Pago ejecutado exitosamente',
      id: 'op-1',
    });

    expect(tx.pagos_proveedor.create).toHaveBeenCalledTimes(1);
    expect(tx.cuentas_pagar.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'cxp-1' },
        data: expect.objectContaining({
          estado: 'pagada',
          saldo_pendiente: 0,
        }),
      }),
    );
    expect(tx.orden_pago_proveedor_cab.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'op-1' },
        data: expect.objectContaining({ estado: 'PAGADO' }),
      }),
    );
    expect(contabilidadIntegracion.integrarOrdenPago).toHaveBeenCalledWith('op-1');
  });

  it('ejecutar crea movimiento en Tesorería cuando existe regla PAGO_PROVEEDOR', async () => {
    const ordenBorrador = {
      id: 'op-1',
      empresa_id: 'emp-1',
      proveedor_id: 'prov-1',
      usuario_id: 'u-1',
      moneda_id: 'mon-1',
      cotizacion: 1,
      numero_orden_pago: 'OP-0001',
      fecha_pago: new Date('2026-04-27'),
      monto_total: 500,
      referencia: 'Transferencia',
      observaciones: null,
      estado: 'BORRADOR',
      activo: true,
      anulado: false,
      orden_pago_proveedor_det: [
        {
          cuenta_pagar_id: 'cxp-1',
          monto: 500,
          monto_moneda: 500,
          cotizacion: 1,
          medio_pago_id: null,
          referencia: null,
          observaciones: null,
        },
      ],
    };

    const { service, tx } = createService({
      orden_pago_proveedor_cab: {
        findFirst: jest
          .fn()
          .mockResolvedValueOnce(ordenBorrador)
          .mockResolvedValueOnce({ ...ordenBorrador, estado: 'PAGADO' }),
        update: jest.fn().mockResolvedValue(undefined),
      },
      tes_reglas: {
        findFirst: jest.fn().mockResolvedValue({ activo: true, cuenta_id: 'tes-cuenta-1', tipo_tesoreria: null }),
      },
      tes_cuentas: {
        findFirst: jest.fn().mockResolvedValue({ id: 'tes-cuenta-1' }),
        update: jest.fn().mockResolvedValue(undefined),
      },
      tes_categorias: {
        findFirst: jest
          .fn()
          .mockResolvedValueOnce(null)
          .mockResolvedValueOnce({ id: 'cat-egreso-otro' }),
        create: jest.fn(),
      },
      tes_movimientos: {
        findFirst: jest.fn().mockResolvedValue(null),
        findMany: jest.fn().mockResolvedValue([]),
        create: jest.fn().mockResolvedValue({ id: 'tes-mov-1' }),
        updateMany: jest.fn().mockResolvedValue({ count: 0 }),
      },
    });

    await service.ejecutar('op-1', 'emp-1', 'u-1');

    expect(tx.tes_movimientos.create).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({
          empresa_id: 'emp-1',
          cuenta_id: 'tes-cuenta-1',
          tipo: 'EGRESO',
          estado: 'CONFIRMADO',
          origen_tipo: 'ORDEN_PAGO_PROVEEDOR',
          origen_id: 'op-1',
        }),
      }),
    );
    expect(tx.tes_movimiento_det.create).toHaveBeenCalledTimes(1);
    expect(tx.tes_cuentas.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'tes-cuenta-1' },
        data: { saldo_actual: { decrement: 500 } },
      }),
    );
  });

  it('ejecutar bloquea OP en estado no permitido', async () => {
    const { service } = createService({
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          estado: 'ANULADA',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [{ cuenta_pagar_id: 'cxp-1', monto: 500 }],
        }),
        update: jest.fn(),
      },
    });

    await expect(service.ejecutar('op-1', 'emp-1')).rejects.toThrow(BadRequestException);
  });

  it('ejecutar bloquea OP ya PAGADA', async () => {
    const { service } = createService({
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          estado: 'PAGADO',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [{ cuenta_pagar_id: 'cxp-1', monto: 500 }],
        }),
        update: jest.fn(),
      },
    });

    await expect(service.ejecutar('op-1', 'emp-1')).rejects.toThrow('La orden ya fue ejecutada');
  });

  it('ejecutar bloquea OP PENDIENTE_APROBACION con aprobación obligatoria', async () => {
    const { service } = createService({
      config_compras: {
        findUnique: jest.fn().mockResolvedValue({
          empresa_id: 'emp-1',
          habilitar_orden_pago: true,
          workflow_obligatorio: true,
          requiere_aprobacion: true,
          permite_pago_sin_aprobacion: false,
          usar_workflow: true,
        }),
        create: jest.fn(),
      },
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          estado: 'PENDIENTE_APROBACION',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [{ cuenta_pagar_id: 'cxp-1', monto: 500 }],
        }),
        update: jest.fn(),
      },
    });

    await expect(service.ejecutar('op-1', 'emp-1')).rejects.toThrow('La orden aún está pendiente de aprobación');
  });

  it('ejecutar bloquea OP RECHAZADA con aprobación obligatoria', async () => {
    const { service } = createService({
      config_compras: {
        findUnique: jest.fn().mockResolvedValue({
          empresa_id: 'emp-1',
          habilitar_orden_pago: true,
          workflow_obligatorio: true,
          requiere_aprobacion: true,
          permite_pago_sin_aprobacion: false,
          usar_workflow: true,
        }),
        create: jest.fn(),
      },
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          estado: 'RECHAZADA',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [{ cuenta_pagar_id: 'cxp-1', monto: 500 }],
        }),
        update: jest.fn(),
      },
    });

    await expect(service.ejecutar('op-1', 'emp-1')).rejects.toThrow(
      'La orden fue rechazada y debe reenviarse a aprobación',
    );
  });

  it('enviarAprobacion mueve OP de BORRADOR a PENDIENTE_APROBACION cuando el workflow es obligatorio', async () => {
    const { service, tx } = createService({
      config_compras: {
        findUnique: jest.fn().mockResolvedValue({
          empresa_id: 'emp-1',
          habilitar_orden_pago: true,
          workflow_obligatorio: true,
          requiere_aprobacion: true,
          permite_pago_sin_aprobacion: false,
          usar_workflow: true,
        }),
        create: jest.fn(),
      },
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          usuario_id: 'u-1',
          estado: 'BORRADOR',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [],
        }),
        update: jest.fn().mockResolvedValue(undefined),
      },
    });

    await service.enviarAprobacion('op-1', 'emp-1', 'u-1');

    expect(tx.orden_pago_proveedor_cab.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'op-1' },
        data: expect.objectContaining({ estado: 'PENDIENTE_APROBACION' }),
      }),
    );
  });

  it('aprobar mueve OP de PENDIENTE_APROBACION a PENDIENTE', async () => {
    const { service, tx } = createService({
      config_compras: {
        findUnique: jest.fn().mockResolvedValue({
          empresa_id: 'emp-1',
          habilitar_orden_pago: true,
          workflow_obligatorio: true,
          requiere_aprobacion: true,
          permite_pago_sin_aprobacion: false,
          usar_workflow: true,
        }),
        create: jest.fn(),
      },
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          usuario_id: 'u-1',
          estado: 'PENDIENTE_APROBACION',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [],
        }),
        update: jest.fn().mockResolvedValue(undefined),
      },
    });

    await service.aprobar('op-1', 'emp-1', 'u-2');

    expect(tx.orden_pago_proveedor_cab.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'op-1' },
        data: expect.objectContaining({ estado: 'PENDIENTE' }),
      }),
    );
  });

  it('rechazar mueve OP de PENDIENTE_APROBACION a RECHAZADA y guarda motivo', async () => {
    const { service, tx } = createService({
      config_compras: {
        findUnique: jest.fn().mockResolvedValue({
          empresa_id: 'emp-1',
          habilitar_orden_pago: true,
          workflow_obligatorio: true,
          requiere_aprobacion: true,
          permite_pago_sin_aprobacion: false,
          usar_workflow: true,
        }),
        create: jest.fn(),
      },
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          usuario_id: 'u-1',
          estado: 'PENDIENTE_APROBACION',
          observaciones: 'Observación previa',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [],
        }),
        update: jest.fn().mockResolvedValue(undefined),
      },
    });

    await service.rechazar('op-1', 'Falta respaldo', 'emp-1', 'u-2');

    expect(tx.orden_pago_proveedor_cab.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'op-1' },
        data: expect.objectContaining({
          estado: 'RECHAZADA',
          observaciones: expect.stringContaining('Falta respaldo'),
        }),
      }),
    );
  });

  it('ejecutar bloquea OP BORRADOR cuando la configuración exige aprobación obligatoria', async () => {
    const { service } = createService({
      config_compras: {
        findUnique: jest.fn().mockResolvedValue({
          empresa_id: 'emp-1',
          habilitar_orden_pago: true,
          workflow_obligatorio: true,
          requiere_aprobacion: true,
          permite_pago_sin_aprobacion: false,
          usar_workflow: true,
        }),
        create: jest.fn(),
      },
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          estado: 'BORRADOR',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [{ cuenta_pagar_id: 'cxp-1', monto: 500 }],
        }),
        update: jest.fn(),
      },
    });

    await expect(service.ejecutar('op-1', 'emp-1')).rejects.toThrow(BadRequestException);
  });

  it('anular en OP aplicada marca cabecera en ANULADA y revierte pagos vinculados', async () => {
    const { service, tx, contabilidadIntegracion } = createService({
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          usuario_id: 'u-1',
          numero_orden_pago: 'OP-0001',
          estado: 'PAGADO',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [
            {
              cuenta_pagar_id: 'cxp-1',
              monto: 500,
            },
          ],
        }),
        update: jest.fn().mockResolvedValue(undefined),
      },
      cuentas_pagar: {
        findUnique: jest.fn().mockResolvedValue({
          id: 'cxp-1',
          compra_id: null,
          origen_tipo: 'compra',
          origen_id: 'comp-1',
          monto_pagado: 500,
          saldo_pendiente: 0,
          monto_original: 500,
        }),
        update: jest.fn().mockResolvedValue(undefined),
      },
      pagos_proveedor: {
        updateMany: jest.fn().mockResolvedValue({ count: 1 }),
      },
    });

    await expect(
      service.anular(
        'op-1',
        {
          motivo: 'Reversa de prueba',
        } as any,
        'emp-1',
        'u-1',
      ),
    ).resolves.toEqual({
      message: 'Orden de pago anulada exitosamente',
      id: 'op-1',
    });

    expect(tx.cuentas_pagar.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'cxp-1' },
        data: expect.objectContaining({
          estado: 'pendiente',
        }),
      }),
    );
    expect(tx.pagos_proveedor.updateMany).toHaveBeenCalledTimes(1);
    expect(tx.orden_pago_proveedor_cab.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'op-1' },
        data: expect.objectContaining({ estado: 'ANULADA', anulado: true }),
      }),
    );
    expect(contabilidadIntegracion.revertirDocumento).toHaveBeenCalledWith('orden_pago_proveedor_cab', 'op-1', 'u-1');
  });

  it('reintentarContabilidad invoca integrarOrdenPago para OP en estado PAGADO', async () => {
    const prisma = {
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({ id: 'op-1', anulado: false, estado: 'PAGADO' }),
      },
    };
    const contabilidadIntegracion = {
      integrarOrdenPago: jest.fn().mockResolvedValue(undefined),
    };
    const service = new PagosProveedorService(prisma as any, contabilidadIntegracion as any);
    jest.spyOn(service, 'findOne').mockResolvedValue({ id: 'op-1' } as any);

    await service.reintentarContabilidad('op-1', 'emp-1');

    expect(contabilidadIntegracion.integrarOrdenPago).toHaveBeenCalledWith('op-1');
  });

  it('reintentarContabilidad rechaza OP que no esté en estado PAGADO', async () => {
    const prisma = {
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({ id: 'op-1', anulado: false, estado: 'PENDIENTE' }),
      },
    };
    const contabilidadIntegracion = {
      integrarOrdenPago: jest.fn(),
    };
    const service = new PagosProveedorService(prisma as any, contabilidadIntegracion as any);

    await expect(service.reintentarContabilidad('op-1', 'emp-1')).rejects.toThrow(
      'Solo se contabilizan órdenes de pago en estado PAGADO',
    );
    expect(contabilidadIntegracion.integrarOrdenPago).not.toHaveBeenCalled();
  });

  it('reintentarContabilidad rechaza OP anulada', async () => {
    const prisma = {
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({ id: 'op-1', anulado: true, estado: 'PAGADO' }),
      },
    };
    const contabilidadIntegracion = { integrarOrdenPago: jest.fn() };
    const service = new PagosProveedorService(prisma as any, contabilidadIntegracion as any);

    await expect(service.reintentarContabilidad('op-1', 'emp-1')).rejects.toThrow(BadRequestException);
    expect(contabilidadIntegracion.integrarOrdenPago).not.toHaveBeenCalled();
  });

  it('anular revierte movimiento de Tesorería vinculado a la OP', async () => {
    const { service, tx } = createService({
      orden_pago_proveedor_cab: {
        findFirst: jest.fn().mockResolvedValue({
          id: 'op-1',
          empresa_id: 'emp-1',
          proveedor_id: 'prov-1',
          usuario_id: 'u-1',
          numero_orden_pago: 'OP-0001',
          estado: 'PAGADO',
          activo: true,
          anulado: false,
          orden_pago_proveedor_det: [{ cuenta_pagar_id: 'cxp-1', monto: 500 }],
        }),
        update: jest.fn().mockResolvedValue(undefined),
      },
      tes_movimientos: {
        findFirst: jest.fn().mockResolvedValue(null),
        findMany: jest.fn().mockResolvedValue([{ id: 'mov-1', cuenta_id: 'tes-cuenta-1', monto: 500 }]),
        create: jest.fn().mockResolvedValue({ id: 'tes-mov-1' }),
        updateMany: jest.fn().mockResolvedValue({ count: 1 }),
      },
      tes_cuentas: {
        findFirst: jest.fn().mockResolvedValue(null),
        update: jest.fn().mockResolvedValue(undefined),
      },
    });

    await service.anular('op-1', { motivo: 'Reversa OP' } as any, 'emp-1', 'u-1');

    expect(tx.tes_movimientos.updateMany).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: { in: ['mov-1'] } },
        data: expect.objectContaining({
          estado: 'ANULADO',
          motivo_anulacion: 'Reversa OP',
        }),
      }),
    );
    expect(tx.tes_cuentas.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'tes-cuenta-1' },
        data: { saldo_actual: { increment: 500 } },
      }),
    );
  });
});
