import { CronogramaError, generarCronograma, sumarMeses, totalCronograma, TramoPlan } from './cronograma';

describe('generarCronograma', () => {
  it('ejemplo del spec: departamento en pozo de USD 80.000', () => {
    const tramos: TramoPlan[] = [
      { orden: 1, tipo: 'SENA', base: 'MONTO', valor: 2000 },
      { orden: 2, tipo: 'ENTREGA', base: 'MONTO', valor: 14000 },
      { orden: 3, tipo: 'CUOTA', base: 'MONTO', valor: 1000, cantidad: 30, inicio_meses: 1 },
      { orden: 4, tipo: 'REFUERZO', base: 'MONTO', valor: 3500, cantidad: 4, periodicidad_meses: 6, inicio_meses: 6 },
      { orden: 5, tipo: 'POSESION', base: 'SALDO', valor: 0 },
    ];
    const c = generarCronograma(80000, tramos, '2026-11-15', 2);

    expect(c).toHaveLength(1 + 1 + 30 + 4 + 1);
    expect(totalCronograma(c, 2)).toBe(80000);
    expect(c.at(-1)).toEqual(expect.objectContaining({ tipo: 'POSESION', monto: 20000, fecha_vencimiento: null }));
    expect(c[2]).toEqual(expect.objectContaining({ descripcion: 'Cuota 1/30', fecha_vencimiento: '2026-12-15' }));
    expect(c.filter((x) => x.tipo === 'REFUERZO').map((x) => x.fecha_vencimiento)).toEqual([
      '2027-05-15',
      '2027-11-15',
      '2028-05-15',
      '2028-11-15',
    ]);
    expect(c.map((x) => x.nro)).toEqual(c.map((_, i) => i + 1));
  });

  it('porcentajes en guaraníes: el residuo de redondeo no se pierde', () => {
    const c = generarCronograma(
      100_000_001,
      [
        { orden: 1, tipo: 'SENA', base: 'PORCENTAJE', valor: 10 },
        { orden: 2, tipo: 'CUOTA', base: 'PORCENTAJE', valor: 90, cantidad: 7, inicio_meses: 1 },
      ],
      '2026-10-10',
    );
    expect(totalCronograma(c)).toBe(100_000_001);
    expect(c.every((x) => Number.isInteger(x.monto))).toBe(true);
  });

  it('cuota contra hito: sin fecha y con el hito', () => {
    const c = generarCronograma(
      1000,
      [
        { tipo: 'SENA', base: 'PORCENTAJE', valor: 10, orden: 1 },
        { tipo: 'HITO', base: 'SALDO', valor: 0, hito_id: 'h-1', descripcion: 'Losa piso 10', orden: 2 },
      ],
      '2026-10-10',
    );
    expect(c[1]).toEqual(
      expect.objectContaining({ fecha_vencimiento: null, hito_id: 'h-1', descripcion: 'Losa piso 10', monto: 900 }),
    );
  });

  it('tramos que no cierran con el precio: error claro', () => {
    expect(() =>
      generarCronograma(1000, [{ tipo: 'CUOTA', base: 'MONTO', valor: 100, cantidad: 3 }], '2026-10-10'),
    ).toThrow(/suman 300 y el precio es 1000/);
    expect(() =>
      generarCronograma(
        1000,
        [
          { tipo: 'CUOTA', base: 'MONTO', valor: 600, cantidad: 2 },
          { tipo: 'POSESION', base: 'SALDO', valor: 0 },
        ],
        '2026-10-10',
      ),
    ).toThrow(CronogramaError);
    expect(() => generarCronograma(1000, [{ tipo: 'HITO', base: 'SALDO', valor: 0 }], '2026-10-10')).toThrow(/hito/);
  });

  it('dos tramos saldo no tienen sentido', () => {
    expect(() =>
      generarCronograma(
        1000,
        [
          { tipo: 'CUOTA', base: 'SALDO', valor: 0 },
          { tipo: 'POSESION', base: 'SALDO', valor: 0 },
        ],
        '2026-10-10',
      ),
    ).toThrow(/un tramo "saldo"/);
  });
});

describe('sumarMeses', () => {
  it('fin de mes y cambio de año', () => {
    expect(sumarMeses('2026-01-31', 1)).toBe('2026-02-28');
    expect(sumarMeses('2027-12-15', 1)).toBe('2028-01-15');
    expect(sumarMeses('2028-01-31', 1)).toBe('2028-02-29');
  });
});
