import { sincronizarCuentaCobrar, type TxSincronizacion } from './sincronizar-cuenta.util';

/** Transacción falsa: registra lo que se escribió, sin base de datos. */
function fakeTx(cuenta: any, cuotas: any[]) {
  const updates: any[] = [];
  const raws: any[] = [];
  const tx: TxSincronizacion = {
    cuentas_cobrar: {
      findFirst: async () => cuenta,
      update: async (args: any) => {
        updates.push(args);
        return args;
      },
    },
    factura_cuotas: { findMany: async () => cuotas },
    $executeRaw: (async (_s: TemplateStringsArray, ...v: any[]) => {
      raws.push(v);
      return 1;
    }) as any,
  };
  return { tx, updates, raws };
}

describe('sincronizarCuentaCobrar', () => {
  it('baja el saldo de la cuenta a la suma de sus cuotas', async () => {
    const { tx, updates } = fakeTx({ id: 'c1', cliente_id: 'cli', saldo_pendiente: 1000 }, [
      { saldo_pendiente: 300, estado: 'pendiente' },
    ]);
    await sincronizarCuentaCobrar(tx, 'fac-1');
    expect(updates[0].data).toMatchObject({ saldo_pendiente: 300, estado: 'pendiente' });
  });

  it('mueve el saldo del cliente por la diferencia, no por el total', async () => {
    const { tx, raws } = fakeTx({ id: 'c1', cliente_id: 'cli', saldo_pendiente: 1000 }, [
      { saldo_pendiente: 300, estado: 'pendiente' },
    ]);
    await sincronizarCuentaCobrar(tx, 'fac-1');
    expect(raws[0][0]).toBe(-700);
  });

  it('no escribe nada si la cuenta ya estaba alineada', async () => {
    const { tx, updates, raws } = fakeTx({ id: 'c1', cliente_id: 'cli', saldo_pendiente: 300 }, [
      { saldo_pendiente: 300, estado: 'pendiente' },
    ]);
    await sincronizarCuentaCobrar(tx, 'fac-1');
    expect(updates).toHaveLength(0);
    expect(raws).toHaveLength(0);
  });

  it('sin cuenta para esa factura no hace nada', async () => {
    const { tx, updates } = fakeTx(null, []);
    await sincronizarCuentaCobrar(tx, 'fac-1');
    expect(updates).toHaveLength(0);
  });

  it('todo saldado deja la cuenta en pagado', async () => {
    const { tx, updates } = fakeTx({ id: 'c1', cliente_id: 'cli', saldo_pendiente: 500 }, [
      { saldo_pendiente: 0, estado: 'pagado' },
    ]);
    await sincronizarCuentaCobrar(tx, 'fac-1');
    expect(updates[0].data.estado).toBe('pagado');
  });

  it('NO pisa el estado de una cuenta anulada', async () => {
    // La anulación de factura marca la cuenta como 'anulada' y después sincroniza
    // el saldo. Si el sincronizador escribiera 'pagado', la cuenta dejaría de
    // verse como anulada en la cartera.
    const { tx, updates } = fakeTx({ id: 'c1', cliente_id: 'cli', saldo_pendiente: 900, estado: 'anulada' }, [
      { saldo_pendiente: 900, estado: 'ANULADA' },
    ]);
    await sincronizarCuentaCobrar(tx, 'fac-1');
    expect(updates[0].data.saldo_pendiente).toBe(0);
    expect(updates[0].data.estado).toBeUndefined();
  });

  it('una cuenta anulada igual devuelve su deuda al cliente', async () => {
    const { tx, raws } = fakeTx({ id: 'c1', cliente_id: 'cli', saldo_pendiente: 900, estado: 'anulada' }, [
      { saldo_pendiente: 900, estado: 'ANULADA' },
    ]);
    await sincronizarCuentaCobrar(tx, 'fac-1');
    expect(raws[0][0]).toBe(-900);
  });

  it('sin cliente en la cuenta no toca el saldo del cliente', async () => {
    const { tx, updates, raws } = fakeTx({ id: 'c1', cliente_id: null, saldo_pendiente: 1000 }, [
      { saldo_pendiente: 300, estado: 'pendiente' },
    ]);
    await sincronizarCuentaCobrar(tx, 'fac-1');
    expect(updates).toHaveLength(1);
    expect(raws).toHaveLength(0);
  });
});
