/**
 * T007b: the previously-dead spending model (EXPENSE_MODIFIERS + SAVINGS_RATES
 * in stats_manager) is now wired into the live applyWeeklyFinances. Lifestyle
 * must measurably change weekly net cashflow, scaling rent must compute across
 * income levels, and owned-item upkeep must be charged.
 */
import { describe, it, expect } from 'vitest';
import { Person } from '../../src/models/Person.js';
import {
  applyWeeklyFinances,
  computeWeeklyFinances,
  sumWeeklyUpkeep,
} from '../../src/game/finance.js';

function worker(spendingHabits: string, salary = 2000, money = 1000): Person {
  return new Person({
    id: 'w',
    firstname: 'W',
    lastname: 'K',
    sex: 'Male',
    occupation: 'engineer',
    salary,
    money,
    spendingHabits,
  } as never);
}

describe('lifestyle modifiers change weekly net cashflow', () => {
  it('frugal nets more than normal, normal more than extravagant', () => {
    const frugal = computeWeeklyFinances(worker('frugal'));
    const normal = computeWeeklyFinances(worker('normal'));
    const extravagant = computeWeeklyFinances(worker('extravagant'));

    expect(frugal.net).toBeGreaterThan(normal.net);
    expect(normal.net).toBeGreaterThan(extravagant.net);
  });

  it('frugal pays lower fixed expenses than extravagant for the same income', () => {
    const frugal = computeWeeklyFinances(worker('frugal'));
    const extravagant = computeWeeklyFinances(worker('extravagant'));
    // Same gross/rent, but extravagant has the 1.5x modifier vs frugal's 0.7x.
    expect(frugal.fixedExpenses).toBeLessThan(extravagant.fixedExpenses);
  });

  it('applying the week mutates money by the computed net', () => {
    const p = worker('normal', 2000, 1000);
    const { net } = computeWeeklyFinances(p);
    applyWeeklyFinances(p);
    expect(p.money).toBeCloseTo(1000 + net, 5);
  });

  it('unset spendingHabits defaults to normal', () => {
    const p = new Person({
      id: 'd',
      firstname: 'D',
      lastname: 'F',
      sex: 'Female',
      occupation: 'engineer',
      salary: 2000,
      money: 1000,
    } as never);
    const def = computeWeeklyFinances(p);
    const normal = computeWeeklyFinances(worker('normal'));
    expect(def.net).toBeCloseTo(normal.net, 5);
  });
});

describe('scaling rent computed correctly across income levels', () => {
  it('low earner is rent-floored, high earner is rent-capped', () => {
    const low = computeWeeklyFinances(worker('normal', 200)); // gross 50 -> rent floor 50
    const high = computeWeeklyFinances(worker('normal', 100_000)); // gross 25k -> rent cap 1250
    expect(low.rent).toBe(50);
    expect(high.rent).toBe(1250);
  });

  it('mid earner pays 30% of weekly income in rent', () => {
    const mid = computeWeeklyFinances(worker('normal', 2000)); // gross 500
    expect(mid.rent).toBe(150);
  });
});

describe('lifestyle upkeep sink (owned items)', () => {
  it('sums weeklyUpkeep across owned items', () => {
    const p = worker('normal');
    p.items = [
      { id: 'car', name: 'Sports Car', weeklyUpkeep: 50 },
      { id: 'watch', name: 'Luxury Watch', weeklyUpkeep: 25 },
      { id: 'free', name: 'No Upkeep Item' },
    ];
    expect(sumWeeklyUpkeep(p)).toBe(75);
  });

  it('upkeep increases fixed expenses and lowers net', () => {
    const without = computeWeeklyFinances(worker('normal'));
    const p = worker('normal');
    p.items = [{ id: 'car', name: 'Sports Car', weeklyUpkeep: 100 }];
    const withUpkeep = computeWeeklyFinances(p);

    expect(withUpkeep.upkeep).toBe(100);
    expect(withUpkeep.fixedExpenses).toBeGreaterThan(without.fixedExpenses);
    expect(withUpkeep.net).toBeLessThan(without.net);
  });
});
