import { emparejarLineasConMovimientos } from './conciliacion-matching';

const dia = (d: number) => new Date(2026, 7, d); // agosto 2026

describe('emparejarLineasConMovimientos', () => {
  it('empareja por comprobante aunque la fecha del movimiento esté más lejos', () => {
    // Caso real (Banco Familiar, Comercial Kety): tres créditos de 150.000, el
    // comprobante del banco aparece como descripción del movimiento. Por fecha
    // sola quedaban cruzados: cada línea con el movimiento de otra.
    const lineas = [
      { id: 'L1', fecha: dia(17), monto: 150000, tipo: 'INGRESO' as const, referencia: '455092209', descripcion: 'CRED-TRANSFERENCIA' },
      { id: 'L2', fecha: dia(17), monto: 150000, tipo: 'INGRESO' as const, referencia: '455092913', descripcion: 'CRED-TRANSFERENCIA' },
      { id: 'L3', fecha: dia(17), monto: 150000, tipo: 'INGRESO' as const, referencia: '456704677', descripcion: 'CRED-TRANSFERENCIA' },
    ];
    const movs = [
      { id: 'M1', fecha: dia(15), monto: 150000, tipo: 'INGRESO' as const, referencia: null, descripcion: '455092913' },
      { id: 'M2', fecha: dia(15), monto: 150000, tipo: 'INGRESO' as const, referencia: null, descripcion: '455092209' },
      { id: 'M3', fecha: dia(17), monto: 150000, tipo: 'INGRESO' as const, referencia: null, descripcion: '456704677' },
    ];
    const r = emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 });
    expect(r.pares.map((p) => `${p.lineaId}->${p.movimientoId}`).sort()).toEqual(['L1->M2', 'L2->M1', 'L3->M3']);
    expect(r.pares.every((p) => p.criterio === 'REFERENCIA')).toBe(true);
  });

  it('con el número de cheque coincidente, empareja aunque el banco lo debite días después', () => {
    // Caso real: el sistema registra el cheque y el banco lo paga por cámara 8
    // días más tarde. El número identifica el hecho; la fecha no debe descartarlo.
    const lineas = [{ id: 'L1', fecha: dia(25), monto: 30000000, tipo: 'EGRESO' as const, referencia: '26144487', descripcion: 'CHEQUE PAGADO POR CAMARA' }];
    const movs = [{ id: 'M1', fecha: new Date(2026, 8, 2), monto: 30000000, tipo: 'EGRESO' as const, referencia: null, descripcion: 'Débito cheque Nº 26144487 — CPH' }];
    const r = emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 });
    expect(r.pares[0]).toMatchObject({ lineaId: 'L1', movimientoId: 'M1', criterio: 'REFERENCIA' });
  });

  it('la ventana amplia del comprobante no es infinita', () => {
    const lineas = [{ id: 'L1', fecha: dia(1), monto: 500, tipo: 'EGRESO' as const, referencia: '99887766', descripcion: 'X' }];
    const movs = [{ id: 'M1', fecha: new Date(2026, 0, 1), monto: 500, tipo: 'EGRESO' as const, referencia: '99887766', descripcion: 'X' }];
    expect(emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 }).pares).toEqual([]);
  });

  it('sin comprobante en común, usa monto + fecha y no reutiliza un movimiento', () => {
    const lineas = [
      { id: 'L1', fecha: dia(10), monto: 5000, tipo: 'EGRESO' as const, referencia: null, descripcion: 'COMISION' },
      { id: 'L2', fecha: dia(10), monto: 5000, tipo: 'EGRESO' as const, referencia: null, descripcion: 'COMISION' },
    ];
    const movs = [{ id: 'M1', fecha: dia(10), monto: 5000, tipo: 'EGRESO' as const, referencia: null, descripcion: 'COMISION' }];
    const r = emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 });
    expect(r.pares).toHaveLength(1);
    expect(r.sinPareja).toEqual([lineas[1].id]);
  });

  it('prefiere la fecha exacta cuando ningún comprobante coincide', () => {
    const lineas = [{ id: 'L1', fecha: dia(12), monto: 900, tipo: 'INGRESO' as const, referencia: 'X', descripcion: 'DEPOSITO' }];
    const movs = [
      { id: 'M_lejos', fecha: dia(10), monto: 900, tipo: 'INGRESO' as const, referencia: null, descripcion: 'DEPOSITO' },
      { id: 'M_exacto', fecha: dia(12), monto: 900, tipo: 'INGRESO' as const, referencia: null, descripcion: 'DEPOSITO' },
    ];
    const r = emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 });
    expect(r.pares[0].movimientoId).toBe('M_exacto');
  });

  it('no empareja una línea de cheque con el movimiento de OTRO cheque', () => {
    // Caso real: el banco pagó el cheque 25864498, pero en el sistema ese cheque
    // está anulado y quedó confirmado otro de igual importe. Por monto y fecha
    // "encajaban", y la línea terminaba pegada al cheque equivocado.
    const lineas = [{ id: 'L1', fecha: dia(18), monto: 60000000, tipo: 'EGRESO' as const, referencia: '25864498', descripcion: 'CHEQUE PAGADO POR CAMARA' }];
    const movs = [{ id: 'M1', fecha: dia(20), monto: 60000000, tipo: 'EGRESO' as const, referencia: null, descripcion: 'Débito cheque Nº 25681727 — NGO' }];
    const r = emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 });
    expect(r.pares).toEqual([]);
    expect(r.sinPareja).toEqual(['L1']);
  });

  it('una descripción con números que no son cheque no bloquea el emparejado', () => {
    // "DB.AUT.PG.PR 16-4068001 CUO 4/12" trae números que no identifican cheque.
    const lineas = [{ id: 'L1', fecha: dia(5), monto: 13352035, tipo: 'EGRESO' as const, referencia: '582437772', descripcion: 'DB.AUT.PG.PR 16-4068001 CUO 4/12' }];
    const movs = [{ id: 'M1', fecha: dia(5), monto: 13352035, tipo: 'EGRESO' as const, referencia: null, descripcion: 'DB.AUT.PG.PR 16-4068001 CUO 4/12' }];
    expect(emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 }).pares).toHaveLength(1);
  });

  it('no cruza ingresos con egresos ni montos fuera de tolerancia', () => {
    const lineas = [{ id: 'L1', fecha: dia(5), monto: 1000, tipo: 'INGRESO' as const, referencia: null, descripcion: 'X' }];
    const movs = [
      { id: 'M1', fecha: dia(5), monto: 1000, tipo: 'EGRESO' as const, referencia: null, descripcion: 'X' },
      { id: 'M2', fecha: dia(5), monto: 1200, tipo: 'INGRESO' as const, referencia: null, descripcion: 'X' },
    ];
    expect(emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 }).pares).toEqual([]);
  });

  it('una referencia que solo aparece en la descripción del movimiento también cuenta', () => {
    const lineas = [{ id: 'L1', fecha: dia(3), monto: 70000, tipo: 'INGRESO' as const, referencia: '112257817', descripcion: 'CRED-TRANSFERENCIA' }];
    const movs = [{ id: 'M1', fecha: dia(1), monto: 70000, tipo: 'INGRESO' as const, referencia: null, descripcion: 'Transferencia recibida 112257817' }];
    const r = emparejarLineasConMovimientos(lineas, movs, { toleranciaDias: 2, toleranciaMonto: 1 });
    expect(r.pares[0]).toMatchObject({ lineaId: 'L1', movimientoId: 'M1', criterio: 'REFERENCIA' });
  });
});
