import {
  pendienteDeCargo,
  pendientePorCuota,
  repartirCobro,
  cuotasConInteresHuerfano,
  totalPendiente,
  validarCarga,
} from './interes-manual';

describe('validarCarga', () => {
  it('acepta un monto positivo sobre una cuota pendiente', () => {
    expect(validarCarga({ monto: 50000, estadoCuota: 'pendiente' })).toBeNull();
  });

  it('rechaza monto cero o negativo', () => {
    expect(validarCarga({ monto: 0, estadoCuota: 'pendiente' })).toBe('MONTO_INVALIDO');
    expect(validarCarga({ monto: -100, estadoCuota: 'pendiente' })).toBe('MONTO_INVALIDO');
  });

  it('rechaza una cuota ya pagada o anulada', () => {
    expect(validarCarga({ monto: 1000, estadoCuota: 'pagado' })).toBe('CUOTA_PAGADA');
    expect(validarCarga({ monto: 1000, estadoCuota: 'Anulada' })).toBe('CUOTA_PAGADA');
  });

  it('exige motivo solo cuando la empresa lo pide', () => {
    expect(validarCarga({ monto: 1000, estadoCuota: 'pendiente', exigirMotivo: true })).toBe('MOTIVO_REQUERIDO');
    expect(validarCarga({ monto: 1000, motivo: 'Atraso acordado', estadoCuota: 'pendiente', exigirMotivo: true })).toBeNull();
  });
});

describe('pendientePorCuota', () => {
  it('suma varios cargos de la misma cuota', () => {
    const mapa = pendientePorCuota([
      { factura_cuota_id: 'c1', monto: 30000, estado: 'pendiente' },
      { factura_cuota_id: 'c1', monto: 20000, estado: 'pendiente' },
    ]);
    expect(mapa.get('c1')).toBe(50000);
  });

  it('ignora los cobrados y los anulados', () => {
    const mapa = pendientePorCuota([
      { factura_cuota_id: 'c1', monto: 30000, estado: 'cobrado' },
      { factura_cuota_id: 'c1', monto: 20000, estado: 'anulado' },
      { factura_cuota_id: 'c2', monto: 10000, estado: 'pendiente' },
    ]);
    expect(mapa.get('c1')).toBeUndefined();
    expect(mapa.get('c2')).toBe(10000);
  });

  it('acepta Decimal de Prisma', () => {
    expect(pendientePorCuota([{ factura_cuota_id: 'c1', monto: '15000.0000', estado: 'pendiente' }]).get('c1')).toBe(15000);
  });
});

describe('totalPendiente', () => {
  it('suma solo las cuotas que entran en el cobro', () => {
    const cargos = [
      { factura_cuota_id: 'c1', monto: 30000, estado: 'pendiente' as const },
      { factura_cuota_id: 'c2', monto: 20000, estado: 'pendiente' as const },
      { factura_cuota_id: 'c3', monto: 70000, estado: 'pendiente' as const },
    ];
    expect(totalPendiente(cargos, ['c1', 'c2'])).toBe(50000);
  });

  it('sin cargos da cero', () => {
    expect(totalPendiente([], ['c1'])).toBe(0);
  });
});


describe('pendienteDeCargo', () => {
  it('lo que falta cobrar de un cargo cobrado en parte', () => {
    expect(pendienteDeCargo({ monto: 200000, monto_cobrado: 50000, estado: 'pendiente' })).toBe(150000);
  });

  it('un cargo sin cobros vale entero', () => {
    expect(pendienteDeCargo({ monto: 200000, monto_cobrado: 0, estado: 'pendiente' })).toBe(200000);
  });

  it('un cargo cobrado o anulado no debe nada', () => {
    expect(pendienteDeCargo({ monto: 200000, monto_cobrado: 200000, estado: 'cobrado' })).toBe(0);
    expect(pendienteDeCargo({ monto: 200000, monto_cobrado: 0, estado: 'anulado' })).toBe(0);
  });
});

describe('repartirCobro', () => {
  const cargos = [
    { id: 'a', monto: 100000, monto_cobrado: 0, estado: 'pendiente' as const },
    { id: 'b', monto: 50000, monto_cobrado: 20000, estado: 'pendiente' as const },
  ];

  it('sin monto indicado cobra todo lo pendiente', () => {
    expect(repartirCobro(cargos, null)).toEqual([
      { id: 'a', monto: 100000 },
      { id: 'b', monto: 30000 },
    ]);
  });

  it('un cobro parcial se aplica al más viejo primero', () => {
    expect(repartirCobro(cargos, 120000)).toEqual([
      { id: 'a', monto: 100000 },
      { id: 'b', monto: 20000 },
    ]);
  });

  it('nunca reparte más de lo que se debe', () => {
    expect(repartirCobro(cargos, 999999)).toEqual([
      { id: 'a', monto: 100000 },
      { id: 'b', monto: 30000 },
    ]);
  });

  it('cobrar cero no toca ningún cargo', () => {
    expect(repartirCobro(cargos, 0)).toEqual([]);
  });

  it('ignora los cargos que ya no deben nada', () => {
    expect(repartirCobro([{ id: 'c', monto: 10000, monto_cobrado: 10000, estado: 'cobrado' }], null)).toEqual([]);
  });
});

describe('cuotasConInteresHuerfano', () => {
  const cargo = (cuotaId: string | null, estado: 'pendiente' | 'cobrado' | 'anulado', monto = 100, cobrado = 0) => ({
    factura_cuota_id: cuotaId,
    monto,
    monto_cobrado: cobrado,
    estado,
  });

  it('devuelve la cuota saldada que todavía debe interés', () => {
    expect(cuotasConInteresHuerfano(['c1'], [cargo('c1', 'pendiente')])).toEqual(['c1']);
  });

  it('ignora la cuota saldada cuyo interés ya se cobró entero', () => {
    expect(cuotasConInteresHuerfano(['c1'], [cargo('c1', 'cobrado', 100, 100)])).toEqual([]);
  });

  it('ignora la cuota que no quedó saldada', () => {
    expect(cuotasConInteresHuerfano([], [cargo('c1', 'pendiente')])).toEqual([]);
  });

  // Review Focus 3: el interés de la cuenta no cuelga de ninguna cuota y no se toca.
  it('nunca devuelve nada por un cargo a nivel cuenta', () => {
    expect(cuotasConInteresHuerfano(['c1'], [cargo(null, 'pendiente')])).toEqual([]);
  });

  it('un cargo cobrado en parte todavía cuenta como huérfano', () => {
    expect(cuotasConInteresHuerfano(['c1'], [cargo('c1', 'pendiente', 100, 60)])).toEqual(['c1']);
  });

  it('sin duplicados cuando la cuota tiene varios cargos pendientes', () => {
    expect(cuotasConInteresHuerfano(['c1'], [cargo('c1', 'pendiente'), cargo('c1', 'pendiente')])).toEqual(['c1']);
  });
});
