const test = require('node:test');
const assert = require('node:assert/strict');

const { buildSafeToSpend } = require('../services/trackerService');

function row(overrides = {}) {
  return {
    id: 1,
    name: 'Bill',
    due_date: '2026-06-12',
    status: 'upcoming',
    balance: 50,
    ...overrides,
  };
}

test('safe to spend subtracts bills still due before the next payday', () => {
  const result = buildSafeToSpend({
    activeRows: [
      row({ id: 1, name: 'Rent', due_date: '2026-06-12', balance: 800 }),
      row({ id: 2, name: 'Power', due_date: '2026-06-14', balance: 120.5 }),
      row({ id: 3, name: 'Paid one', due_date: '2026-06-13', status: 'paid', balance: 0 }),
      row({ id: 4, name: 'After payday', due_date: '2026-06-20', balance: 60 }),
    ],
    available: 1500,
    todayStr: '2026-06-10',
    year: 2026,
    month: 6,
    dayOfMonth: 10,
  });

  assert.equal(result.next_payday, '2026-06-15');
  assert.equal(result.days_until_payday, 5);
  assert.equal(result.still_due_count, 2);
  assert.equal(result.still_due_total, 920.5);
  assert.equal(result.safe_to_spend, 579.5);
  assert.deepEqual(result.upcoming.map(u => u.name), ['Rent', 'Power']);
});

test('second half of the month rolls payday to the 1st, December wraps the year', () => {
  const june = buildSafeToSpend({
    activeRows: [], available: 100, todayStr: '2026-06-20', year: 2026, month: 6, dayOfMonth: 20,
  });
  assert.equal(june.next_payday, '2026-07-01');

  const dec = buildSafeToSpend({
    activeRows: [], available: 100, todayStr: '2026-12-20', year: 2026, month: 12, dayOfMonth: 20,
  });
  assert.equal(dec.next_payday, '2027-01-01');
  assert.equal(dec.days_until_payday, 12);
});

test('overdue bills count against safe-to-spend and land on today in the timeline', () => {
  const result = buildSafeToSpend({
    activeRows: [
      row({ id: 1, name: 'Late card', due_date: '2026-06-03', status: 'late', balance: 200 }),
      row({ id: 2, name: 'Internet', due_date: '2026-06-13', balance: 80 }),
    ],
    available: 500,
    todayStr: '2026-06-10',
    year: 2026,
    month: 6,
    dayOfMonth: 10,
  });

  assert.equal(result.safe_to_spend, 220);
  // Overdue bill is charged on today's entry, not in the past
  const todayEntry = result.timeline.find(t => t.date === '2026-06-10');
  assert.equal(todayEntry.balance, 300);
  assert.deepEqual(todayEntry.bills.map(b => b.name), ['Late card']);
  // Timeline ends on payday
  assert.equal(result.timeline[result.timeline.length - 1].date, '2026-06-15');
  assert.equal(result.timeline[result.timeline.length - 1].balance, 220);
});

test('cent-exact math: no float drift across many bills', () => {
  const bills = Array.from({ length: 30 }, (_, i) =>
    row({ id: i, name: `B${i}`, due_date: '2026-06-12', balance: 0.1 }));
  const result = buildSafeToSpend({
    activeRows: bills, available: 10, todayStr: '2026-06-10', year: 2026, month: 6, dayOfMonth: 10,
  });
  assert.equal(result.still_due_total, 3);
  assert.equal(result.safe_to_spend, 7);
});

test('negative safe-to-spend is reported, not clamped', () => {
  const result = buildSafeToSpend({
    activeRows: [row({ balance: 300 })],
    available: 100, todayStr: '2026-06-10', year: 2026, month: 6, dayOfMonth: 10,
  });
  assert.equal(result.safe_to_spend, -200);
});

test('skipped/zero-balance rows and empty data produce a flat timeline', () => {
  const result = buildSafeToSpend({
    activeRows: [row({ status: 'paid', balance: 0 })],
    available: 250, todayStr: '2026-06-10', year: 2026, month: 6, dayOfMonth: 10,
  });
  assert.equal(result.still_due_count, 0);
  assert.equal(result.safe_to_spend, 250);
  assert.equal(result.timeline[0].date, '2026-06-10');
  assert.equal(result.timeline[0].balance, 250);
  assert.ok(result.timeline[result.timeline.length - 1].payday);
});
