/**
 * Task 0-A originally extracted weekly finances into the shared
 * `applyWeeklyFinances` so the online (PlayerSession) and offline (GameEngine)
 * paths produce identical deltas.
 *
 * Task T007b (fun/balance) replaced the trivial flat-$50 economy with one that
 * has real resource tension:
 *   - income = salary / 4 for working occupations (not student/preschool)
 *   - SCALING rent = clamp(weeklyIncome * 0.30, $50, $1250) instead of flat $50
 *   - lifestyle (frugal/normal/extravagant) modifies fixed expenses
 *     (EXPENSE_MODIFIERS 0.7/1.0/1.5) AND the savings skim (SAVINGS_RATES
 *     0.20/0.10/0.05)
 *   - owned-item weekly upkeep is summed in as an ongoing sink
 *   - net = surplus > 0 ? surplus * savingsRate : surplus (surplus = income -
 *     fixedExpenses); money floored at 0
 *
 * These expectations were UPDATED from the old flat-$50 numbers to the new
 * intended economy (see receipt notes). Parity (online == offline) is preserved.
 */
import { describe, it, expect } from 'vitest';
import { Person } from '../../src/models/Person.js';
import {
  applyWeeklyFinances,
  computeWeeklyFinances,
  WEEKLY_EXPENSES,
} from '../../src/game/finance.js';

function makeWorker(money: number, salary: number, spendingHabits = 'normal'): Person {
  return new Person({
    id: 'worker-1',
    firstname: 'Work',
    lastname: 'Er',
    sex: 'Male',
    occupation: 'engineer',
    salary,
    money,
    spendingHabits,
  } as never);
}

describe('applyWeeklyFinances (shared weekly economy with real tension)', () => {
  it('normal worker: income - scaling rent, then savings-rate skim of surplus', () => {
    // gross 500, rent clamp(150,50,1250)=150, modifier 1.0 -> fixed 150
    // surplus 350, net = 350 * 0.35 (normal savings, raised for meaningful wealth) = 122.5
    const p = makeWorker(100, 2000, 'normal');
    const b = computeWeeklyFinances(p);
    expect(b.grossIncome).toBe(500);
    expect(b.rent).toBe(150);
    expect(b.fixedExpenses).toBe(150);
    expect(b.surplus).toBe(350);
    expect(b.net).toBeCloseTo(122.5, 5);

    applyWeeklyFinances(p);
    expect(p.money).toBeCloseTo(222.5, 5);
  });

  it('WEEKLY_EXPENSES is retained as the rent floor', () => {
    expect(WEEKLY_EXPENSES).toBe(50);
  });

  it('non-earning occupation (student) only owes the rent floor', () => {
    // gross 0, rent floor 50, surplus -50, net -50
    const student = new Person({
      id: 'kid-1',
      firstname: 'Stu',
      lastname: 'Dent',
      sex: 'Female',
      occupation: 'student',
      salary: 9999, // ignored: students do not earn
      money: 200,
    } as never);
    applyWeeklyFinances(student);
    expect(student.money).toBe(150); // 200 - 50
  });

  it('preschooler does not earn either (owes rent floor)', () => {
    const child = new Person({
      id: 'tot-1',
      firstname: 'Tot',
      lastname: 'Kid',
      sex: 'Male',
      occupation: 'preschool',
      salary: 5000,
      money: 60,
    } as never);
    applyWeeklyFinances(child);
    expect(child.money).toBe(10); // 60 - 50
  });

  it('a broke person never goes negative (money floored at 0)', () => {
    const broke = new Person({
      id: 'broke-1',
      firstname: 'No',
      lastname: 'Cash',
      sex: 'Male',
      occupation: 'student',
      money: 30, // less than the $50 rent floor
    } as never);
    applyWeeklyFinances(broke);
    expect(broke.money).toBe(0);
    expect(broke.money).toBeGreaterThanOrEqual(0);
  });

  it('a low earner with no buffer stays >= 0', () => {
    // gross 25, rent floor 50, surplus -25, net -25 -> floored at 0
    const p = makeWorker(0, 100, 'normal');
    applyWeeklyFinances(p);
    expect(p.money).toBe(0);
  });
});
